/*-------------------------------------------------------------------------
 *
 * aclchk.c
 *	  检查访问控制权限的常规例程。
 *
 * Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group
 * Portions Copyright (c) 1994, Regents of the University of California
 *
 *
 * IDENTIFICATION
 *	  src/backend/catalog/aclchk.c
 *
 * NOTES
 *	  参见acl.h。
 *
 *-------------------------------------------------------------------------
 */
#include "postgres.h"

#include "access/genam.h"
#include "access/heapam.h"
#include "access/htup_details.h"
#include "access/sysattr.h"
#include "access/tableam.h"
#include "access/xact.h"
#include "catalog/binary_upgrade.h"
#include "catalog/catalog.h"
#include "catalog/dependency.h"
#include "catalog/indexing.h"
#include "catalog/objectaccess.h"
#include "catalog/pg_aggregate.h"
#include "catalog/pg_am.h"
#include "catalog/pg_authid.h"
#include "catalog/pg_cast.h"
#include "catalog/pg_collation.h"
#include "catalog/pg_conversion.h"
#include "catalog/pg_database.h"
#include "catalog/pg_default_acl.h"
#include "catalog/pg_event_trigger.h"
#include "catalog/pg_extension.h"
#include "catalog/pg_foreign_data_wrapper.h"
#include "catalog/pg_foreign_server.h"
#include "catalog/pg_init_privs.h"
#include "catalog/pg_language.h"
#include "catalog/pg_largeobject.h"
#include "catalog/pg_largeobject_metadata.h"
#include "catalog/pg_namespace.h"
#include "catalog/pg_opclass.h"
#include "catalog/pg_operator.h"
#include "catalog/pg_opfamily.h"
#include "catalog/pg_parameter_acl.h"
#include "catalog/pg_proc.h"
#include "catalog/pg_statistic_ext.h"
#include "catalog/pg_subscription.h"
#include "catalog/pg_tablespace.h"
#include "catalog/pg_transform.h"
#include "catalog/pg_ts_config.h"
#include "catalog/pg_ts_dict.h"
#include "catalog/pg_ts_parser.h"
#include "catalog/pg_ts_template.h"
#include "catalog/pg_type.h"
#include "commands/dbcommands.h"
#include "commands/defrem.h"
#include "commands/event_trigger.h"
#include "commands/extension.h"
#include "commands/proclang.h"
#include "commands/tablespace.h"
#include "foreign/foreign.h"
#include "miscadmin.h"
#include "nodes/makefuncs.h"
#include "parser/parse_func.h"
#include "parser/parse_type.h"
#include "storage/lmgr.h"
#include "utils/acl.h"
#include "utils/aclchk_internal.h"
#include "utils/builtins.h"
#include "utils/fmgroids.h"
#include "utils/lsyscache.h"
#include "utils/rel.h"
#include "utils/syscache.h"

/*
 * ALTER DEFAULT PRIVILEGES 使用的内部格式。
 */
typedef struct
{
	Oid			roleid;			/* 拥有角色 */
	Oid			nspid;			/* 名称空间，如果没有则为 InvalidOid */
	/* 剩余字段与 InternalGrant 中相同： */
	bool		is_grant;
	ObjectType	objtype;
	bool		all_privs;
	AclMode		privileges;
	List	   *grantees;
	bool		grant_option;
	DropBehavior behavior;
} InternalDefaultACL;

/*
 * 在执行二进制升级时，pg_dump 将调用一个函数来设置这个变量，以便让我们知道我们需要在这个变量设置为 true 时填充 pg_init_privs 表，以支持 GRANT/REVOKE 命令。
 */
bool		binary_upgrade_record_init_privs = false;

static void fc_ExecGrantStmt_oids(InternalGrant *fc_istmt);
static void fc_ExecGrant_Relation(InternalGrant *fc_grantStmt);
static void fc_ExecGrant_Database(InternalGrant *fc_grantStmt);
static void fc_ExecGrant_Fdw(InternalGrant *fc_grantStmt);
static void fc_ExecGrant_ForeignServer(InternalGrant *fc_grantStmt);
static void fc_ExecGrant_Function(InternalGrant *fc_grantStmt);
static void fc_ExecGrant_Language(InternalGrant *fc_grantStmt);
static void fc_ExecGrant_Largeobject(InternalGrant *fc_grantStmt);
static void fc_ExecGrant_Namespace(InternalGrant *fc_grantStmt);
static void fc_ExecGrant_Tablespace(InternalGrant *fc_grantStmt);
static void fc_ExecGrant_Type(InternalGrant *fc_grantStmt);
static void fc_ExecGrant_Parameter(InternalGrant *fc_grantStmt);

static void fc_SetDefaultACLsInSchemas(InternalDefaultACL *fc_iacls, List *fc_nspnames);
static void fc_SetDefaultACL(InternalDefaultACL *fc_iacls);

static List *fc_objectNamesToOids(ObjectType fc_objtype, List *fc_objnames,
							   bool fc_is_grant);
static List *fc_objectsInSchemaToOids(ObjectType fc_objtype, List *fc_nspnames);
static List *fc_getRelationsInNamespace(Oid fc_namespaceId, char fc_relkind);
static void fc_expand_col_privileges(List *fc_colnames, Oid fc_table_oid,
								  AclMode fc_this_privileges,
								  AclMode *fc_col_privileges,
								  int fc_num_col_privileges);
static void fc_expand_all_col_privileges(Oid fc_table_oid, Form_pg_class fc_classForm,
									  AclMode fc_this_privileges,
									  AclMode *fc_col_privileges,
									  int fc_num_col_privileges);
static AclMode fc_string_to_privilege(const char *fc_privname);
static const char *fc_privilege_to_string(AclMode fc_privilege);
static AclMode fc_restrict_and_check_grant(bool fc_is_grant, AclMode fc_avail_goptions,
										bool fc_all_privs, AclMode fc_privileges,
										Oid fc_objectId, Oid fc_grantorId,
										ObjectType fc_objtype, const char *fc_objname,
										AttrNumber fc_att_number, const char *fc_colname);
static AclMode pg_aclmask(ObjectType fc_objtype, Oid fc_table_oid, AttrNumber fc_attnum,
						  Oid fc_roleid, AclMode fc_mask, AclMaskHow fc_how);
static void fc_recordExtensionInitPriv(Oid fc_objoid, Oid fc_classoid, int fc_objsubid,
									Acl *fc_new_acl);
static void fc_recordExtensionInitPrivWorker(Oid fc_objoid, Oid fc_classoid, int fc_objsubid,
										  Acl *fc_new_acl);


/*
 * 如果 is_grant 为 true，则将给定的权限添加到现有的 old_acl 中的授予者列表。 如果 is_grant 为 false，则从 old_acl 中移除给定授予者的权限。
 *
 * 注意：原始的 old_acl 被 pfree() 处理。
 */
static Acl * fc_merge_acl_with_grant(Acl *fc_old_acl, bool fc_is_grant,
					 bool fc_grant_option, DropBehavior fc_behavior,
					 List *fc_grantees, AclMode fc_privileges,
					 Oid fc_grantorId, Oid fc_ownerId)
{
	unsigned	fc_modechg;
	ListCell   *fc_j;
	Acl		   *fc_new_acl;

	fc_modechg = fc_is_grant ? ACL_MODECHG_ADD : ACL_MODECHG_DEL;

	fc_new_acl = fc_old_acl;

	foreach(fc_j, fc_grantees)
	{
		AclItem		fc_aclitem;
		Acl		   *fc_newer_acl;

		fc_aclitem.ai_grantee = lfirst_oid(fc_j);

		/*
	 * 授予选项只能授予给单独的角色，而不能授予给 PUBLIC。
	 * 原因是，如果用户通过 PUBLIC 持有的权限被重新授予，并且之后该用户被移除，那么情况就无法清理了。
	 */
		if (fc_is_grant && fc_grant_option && fc_aclitem.ai_grantee == ACL_ID_PUBLIC)
			ereport(ERROR,
					(errcode(ERRCODE_INVALID_GRANT_OPERATION),
					 errmsg("grant options can only be granted to roles")));

		fc_aclitem.ai_grantor = fc_grantorId;

		/*
		 * 这里条件的不对称来自规范。在 GRANT 中，grant_option 标志表示 WITH GRANT OPTION，这意味着授予基本权限及其授予选项。 但在 REVOKE 中，普通撤销会撤销基本权限及其授予选项，而 REVOKE GRANT OPTION 仅撤销选项。
		 */
		ACLITEM_SET_PRIVS_GOPTIONS(fc_aclitem,
								   (fc_is_grant || !fc_grant_option) ? fc_privileges : ACL_NO_RIGHTS,
								   (!fc_is_grant || fc_grant_option) ? fc_privileges : ACL_NO_RIGHTS);

		fc_newer_acl = aclupdate(fc_new_acl, &fc_aclitem, fc_modechg, fc_ownerId, fc_behavior);

		/* 当有许多授予者时，避免内存泄漏 */
		pfree(fc_new_acl);
		fc_new_acl = fc_newer_acl;
	}

	return fc_new_acl;
}

/*
 * 将权限限制为我们实际可以授予的，并发出标准规定的警告和错误消息。
 */
static AclMode fc_restrict_and_check_grant(bool fc_is_grant, AclMode fc_avail_goptions, bool fc_all_privs,
						 AclMode fc_privileges, Oid fc_objectId, Oid fc_grantorId,
						 ObjectType fc_objtype, const char *fc_objname,
						 AttrNumber fc_att_number, const char *fc_colname)
{
	AclMode		fc_this_privileges;
	AclMode		fc_whole_mask;

	switch (fc_objtype)
	{
		case OBJECT_COLUMN:
			fc_whole_mask = ACL_ALL_RIGHTS_COLUMN;
			break;
		case OBJECT_TABLE:
			fc_whole_mask = ACL_ALL_RIGHTS_RELATION;
			break;
		case OBJECT_SEQUENCE:
			fc_whole_mask = ACL_ALL_RIGHTS_SEQUENCE;
			break;
		case OBJECT_DATABASE:
			fc_whole_mask = ACL_ALL_RIGHTS_DATABASE;
			break;
		case OBJECT_FUNCTION:
			fc_whole_mask = ACL_ALL_RIGHTS_FUNCTION;
			break;
		case OBJECT_LANGUAGE:
			fc_whole_mask = ACL_ALL_RIGHTS_LANGUAGE;
			break;
		case OBJECT_LARGEOBJECT:
			fc_whole_mask = ACL_ALL_RIGHTS_LARGEOBJECT;
			break;
		case OBJECT_SCHEMA:
			fc_whole_mask = ACL_ALL_RIGHTS_SCHEMA;
			break;
		case OBJECT_TABLESPACE:
			fc_whole_mask = ACL_ALL_RIGHTS_TABLESPACE;
			break;
		case OBJECT_FDW:
			fc_whole_mask = ACL_ALL_RIGHTS_FDW;
			break;
		case OBJECT_FOREIGN_SERVER:
			fc_whole_mask = ACL_ALL_RIGHTS_FOREIGN_SERVER;
			break;
		case OBJECT_EVENT_TRIGGER:
			elog(ERROR, "grantable rights not supported for event triggers");
			/* 未达到，但保持编译器安静 */
			return ACL_NO_RIGHTS;
		case OBJECT_TYPE:
			fc_whole_mask = ACL_ALL_RIGHTS_TYPE;
			break;
		case OBJECT_PARAMETER_ACL:
			fc_whole_mask = ACL_ALL_RIGHTS_PARAMETER_ACL;
			break;
		default:
			elog(ERROR, "unrecognized object type: %d", fc_objtype);
			/* 未达到，但保持编译器安静 */
			return ACL_NO_RIGHTS;
	}

	/*
	 * 如果我们没有找到授予选项，请考虑是否发出严重错误。
	 * 根据规范，拥有对象上的任何权限都能使你通过这里。
	 */
	if (fc_avail_goptions == ACL_NO_RIGHTS)
	{
		if (pg_aclmask(fc_objtype, fc_objectId, fc_att_number, fc_grantorId,
					   fc_whole_mask | ACL_GRANT_OPTION_FOR(fc_whole_mask),
					   ACLMASK_ANY) == ACL_NO_RIGHTS)
		{
			if (fc_objtype == OBJECT_COLUMN && fc_colname)
				aclcheck_error_col(ACLCHECK_NO_PRIV, fc_objtype, fc_objname, fc_colname);
			else
				aclcheck_error(ACLCHECK_NO_PRIV, fc_objtype, fc_objname);
		}
	}

	/*
	 * 将操作限制为我们实际可以授予或撤销的，并在适当时发出警告。 （对于 REVOKE，这与规范的要求不完全一致：规范似乎希望仅在 ACL 中实际上没有权限位发生变化时才发出警告。 实际上，这种行为似乎太嘈杂，同时也与 GRANT 的情况不一致。）
	 */
	fc_this_privileges = fc_privileges & ACL_OPTION_TO_PRIVS(fc_avail_goptions);
	if (fc_is_grant)
	{
		if (fc_this_privileges == 0)
		{
			if (fc_objtype == OBJECT_COLUMN && fc_colname)
				ereport(WARNING,
						(errcode(ERRCODE_WARNING_PRIVILEGE_NOT_GRANTED),
						 errmsg("no privileges were granted for column \"%s\" of relation \"%s\"",
								fc_colname, fc_objname)));
			else
				ereport(WARNING,
						(errcode(ERRCODE_WARNING_PRIVILEGE_NOT_GRANTED),
						 errmsg("no privileges were granted for \"%s\"",
								fc_objname)));
		}
		else if (!fc_all_privs && fc_this_privileges != fc_privileges)
		{
			if (fc_objtype == OBJECT_COLUMN && fc_colname)
				ereport(WARNING,
						(errcode(ERRCODE_WARNING_PRIVILEGE_NOT_GRANTED),
						 errmsg("not all privileges were granted for column \"%s\" of relation \"%s\"",
								fc_colname, fc_objname)));
			else
				ereport(WARNING,
						(errcode(ERRCODE_WARNING_PRIVILEGE_NOT_GRANTED),
						 errmsg("not all privileges were granted for \"%s\"",
								fc_objname)));
		}
	}
	else
	{
		if (fc_this_privileges == 0)
		{
			if (fc_objtype == OBJECT_COLUMN && fc_colname)
				ereport(WARNING,
						(errcode(ERRCODE_WARNING_PRIVILEGE_NOT_REVOKED),
						 errmsg("no privileges could be revoked for column \"%s\" of relation \"%s\"",
								fc_colname, fc_objname)));
			else
				ereport(WARNING,
						(errcode(ERRCODE_WARNING_PRIVILEGE_NOT_REVOKED),
						 errmsg("no privileges could be revoked for \"%s\"",
								fc_objname)));
		}
		else if (!fc_all_privs && fc_this_privileges != fc_privileges)
		{
			if (fc_objtype == OBJECT_COLUMN && fc_colname)
				ereport(WARNING,
						(errcode(ERRCODE_WARNING_PRIVILEGE_NOT_REVOKED),
						 errmsg("not all privileges could be revoked for column \"%s\" of relation \"%s\"",
								fc_colname, fc_objname)));
			else
				ereport(WARNING,
						(errcode(ERRCODE_WARNING_PRIVILEGE_NOT_REVOKED),
						 errmsg("not all privileges could be revoked for \"%s\"",
								fc_objname)));
		}
	}

	return fc_this_privileges;
}

/*
 * 被调用以执行 GRANT 和 REVOKE 实用命令
 */
void ExecuteGrantStmt(GrantStmt *fc_stmt)
{
	InternalGrant fc_istmt;
	ListCell   *fc_cell;
	const char *fc_errormsg;
	AclMode		fc_all_privileges;

	if (fc_stmt->grantor)
	{
		Oid			fc_grantor;

		fc_grantor = get_rolespec_oid(fc_stmt->grantor, false);

		/*
		 * 当前，此条款仅用于 SQL 兼容性，其他方面并不特别有趣。
		 */
		if (fc_grantor != GetUserId())
			ereport(ERROR,
					(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
					 errmsg("grantor must be current user")));
	}

	/*
	 * 将常规 GrantStmt 转换为 InternalGrant 形式。
	 */
	fc_istmt.is_grant = fc_stmt->is_grant;
	fc_istmt.objtype = fc_stmt->objtype;

	/* 收集目标对象的 OID */
	switch (fc_stmt->targtype)
	{
		case ACL_TARGET_OBJECT:
			fc_istmt.objects = fc_objectNamesToOids(fc_stmt->objtype, fc_stmt->objects,
											  fc_stmt->is_grant);
			break;
		case ACL_TARGET_ALL_IN_SCHEMA:
			fc_istmt.objects = fc_objectsInSchemaToOids(fc_stmt->objtype, fc_stmt->objects);
			break;
			/* 这里不应看到 ACL_TARGET_DEFAULTS */
		default:
			elog(ERROR, "unrecognized GrantStmt.targtype: %d",
				 (int) fc_stmt->targtype);
	}

	/* all_privs 将在下面填充 */
	/* privileges 将在下面填充 */
	fc_istmt.col_privs = NIL;		/* 可能在下面填充 */
	fc_istmt.grantees = NIL;		/* 在下面填充 */
	fc_istmt.grant_option = fc_stmt->grant_option;
	fc_istmt.behavior = fc_stmt->behavior;

	/*
	 * 将 RoleSpec 列表转换为 Oid 列表。请注意，此时如果合适，我们将在列表中插入 ACL_ID_PUBLIC，这样下游就不需要额外的工作来支持这种情况。
	 */
	foreach(fc_cell, fc_stmt->grantees)
	{
		RoleSpec   *fc_grantee = (RoleSpec *) lfirst(fc_cell);
		Oid			fc_grantee_uid;

		switch (fc_grantee->roletype)
		{
			case ROLESPEC_PUBLIC:
				fc_grantee_uid = ACL_ID_PUBLIC;
				break;
			default:
				fc_grantee_uid = get_rolespec_oid(fc_grantee, false);
				break;
		}
		fc_istmt.grantees = lappend_oid(fc_istmt.grantees, fc_grantee_uid);
	}

	/*
	 * 将 stmt->privileges（一个 AccessPriv 节点的列表）转换为 AclMode
	 * 位掩码。 注意：objtype 不能是 OBJECT_COLUMN。
	 */
	switch (fc_stmt->objtype)
	{
		case OBJECT_TABLE:

			/*
			 * 因为这可能是一个序列，我们测试关系和
			 * 序列位，稍后在我们知道对象类型时进行更严格的测试。
			 */
			fc_all_privileges = ACL_ALL_RIGHTS_RELATION | ACL_ALL_RIGHTS_SEQUENCE;
			fc_errormsg = gettext_noop("invalid privilege type %s for relation");
			break;
		case OBJECT_SEQUENCE:
			fc_all_privileges = ACL_ALL_RIGHTS_SEQUENCE;
			fc_errormsg = gettext_noop("invalid privilege type %s for sequence");
			break;
		case OBJECT_DATABASE:
			fc_all_privileges = ACL_ALL_RIGHTS_DATABASE;
			fc_errormsg = gettext_noop("invalid privilege type %s for database");
			break;
		case OBJECT_DOMAIN:
			fc_all_privileges = ACL_ALL_RIGHTS_TYPE;
			fc_errormsg = gettext_noop("invalid privilege type %s for domain");
			break;
		case OBJECT_FUNCTION:
			fc_all_privileges = ACL_ALL_RIGHTS_FUNCTION;
			fc_errormsg = gettext_noop("invalid privilege type %s for function");
			break;
		case OBJECT_LANGUAGE:
			fc_all_privileges = ACL_ALL_RIGHTS_LANGUAGE;
			fc_errormsg = gettext_noop("invalid privilege type %s for language");
			break;
		case OBJECT_LARGEOBJECT:
			fc_all_privileges = ACL_ALL_RIGHTS_LARGEOBJECT;
			fc_errormsg = gettext_noop("invalid privilege type %s for large object");
			break;
		case OBJECT_SCHEMA:
			fc_all_privileges = ACL_ALL_RIGHTS_SCHEMA;
			fc_errormsg = gettext_noop("invalid privilege type %s for schema");
			break;
		case OBJECT_PROCEDURE:
			fc_all_privileges = ACL_ALL_RIGHTS_FUNCTION;
			fc_errormsg = gettext_noop("invalid privilege type %s for procedure");
			break;
		case OBJECT_ROUTINE:
			fc_all_privileges = ACL_ALL_RIGHTS_FUNCTION;
			fc_errormsg = gettext_noop("invalid privilege type %s for routine");
			break;
		case OBJECT_TABLESPACE:
			fc_all_privileges = ACL_ALL_RIGHTS_TABLESPACE;
			fc_errormsg = gettext_noop("invalid privilege type %s for tablespace");
			break;
		case OBJECT_TYPE:
			fc_all_privileges = ACL_ALL_RIGHTS_TYPE;
			fc_errormsg = gettext_noop("invalid privilege type %s for type");
			break;
		case OBJECT_FDW:
			fc_all_privileges = ACL_ALL_RIGHTS_FDW;
			fc_errormsg = gettext_noop("invalid privilege type %s for foreign-data wrapper");
			break;
		case OBJECT_FOREIGN_SERVER:
			fc_all_privileges = ACL_ALL_RIGHTS_FOREIGN_SERVER;
			fc_errormsg = gettext_noop("invalid privilege type %s for foreign server");
			break;
		case OBJECT_PARAMETER_ACL:
			fc_all_privileges = ACL_ALL_RIGHTS_PARAMETER_ACL;
			fc_errormsg = gettext_noop("invalid privilege type %s for parameter");
			break;
		default:
			elog(ERROR, "unrecognized GrantStmt.objtype: %d",
				 (int) fc_stmt->objtype);
			/* 保持编译器安静 */
			fc_all_privileges = ACL_NO_RIGHTS;
			fc_errormsg = NULL;
	}

	if (fc_stmt->privileges == NIL)
	{
		fc_istmt.all_privs = true;

		/*
		 * 将根据对象类型由内部例程转换为 ACL_ALL_RIGHTS_*。
		 */
		fc_istmt.privileges = ACL_NO_RIGHTS;
	}
	else
	{
		fc_istmt.all_privs = false;
		fc_istmt.privileges = ACL_NO_RIGHTS;

		foreach(fc_cell, fc_stmt->privileges)
		{
			AccessPriv *fc_privnode = (AccessPriv *) lfirst(fc_cell);
			AclMode		fc_priv;

			/*
			 * 如果这是列级别的规范，我们暂时将其留在
			 * col_privs 中；但必须坚持这是针对关系的。
			 */
			if (fc_privnode->cols)
			{
				if (fc_stmt->objtype != OBJECT_TABLE)
					ereport(ERROR,
							(errcode(ERRCODE_INVALID_GRANT_OPERATION),
							 errmsg("column privileges are only valid for relations")));
				fc_istmt.col_privs = lappend(fc_istmt.col_privs, fc_privnode);
				continue;
			}

			if (fc_privnode->priv_name == NULL)	/* 解析器错误？ */
				elog(ERROR, "AccessPriv node must specify privilege or columns");
			fc_priv = fc_string_to_privilege(fc_privnode->priv_name);

			if (fc_priv & ~((AclMode) fc_all_privileges))
				ereport(ERROR,
						(errcode(ERRCODE_INVALID_GRANT_OPERATION),
						 errmsg(fc_errormsg, fc_privilege_to_string(fc_priv))));

			fc_istmt.privileges |= fc_priv;
		}
	}

	fc_ExecGrantStmt_oids(&fc_istmt);
}

/*
 * ExecGrantStmt_oids
 *
 * 授予和撤销权限的内部入口点。
 */
static void fc_ExecGrantStmt_oids(InternalGrant *fc_istmt)
{
	switch (fc_istmt->objtype)
	{
		case OBJECT_TABLE:
		case OBJECT_SEQUENCE:
			fc_ExecGrant_Relation(fc_istmt);
			break;
		case OBJECT_DATABASE:
			fc_ExecGrant_Database(fc_istmt);
			break;
		case OBJECT_DOMAIN:
		case OBJECT_TYPE:
			fc_ExecGrant_Type(fc_istmt);
			break;
		case OBJECT_FDW:
			fc_ExecGrant_Fdw(fc_istmt);
			break;
		case OBJECT_FOREIGN_SERVER:
			fc_ExecGrant_ForeignServer(fc_istmt);
			break;
		case OBJECT_FUNCTION:
		case OBJECT_PROCEDURE:
		case OBJECT_ROUTINE:
			fc_ExecGrant_Function(fc_istmt);
			break;
		case OBJECT_LANGUAGE:
			fc_ExecGrant_Language(fc_istmt);
			break;
		case OBJECT_LARGEOBJECT:
			fc_ExecGrant_Largeobject(fc_istmt);
			break;
		case OBJECT_SCHEMA:
			fc_ExecGrant_Namespace(fc_istmt);
			break;
		case OBJECT_TABLESPACE:
			fc_ExecGrant_Tablespace(fc_istmt);
			break;
		case OBJECT_PARAMETER_ACL:
			fc_ExecGrant_Parameter(fc_istmt);
			break;
		default:
			elog(ERROR, "unrecognized GrantStmt.objtype: %d",
				 (int) fc_istmt->objtype);
	}

	/*
	 * 将信息传递给事件触发器，关于刚刚执行的 GRANT。注意
	 * 我们更喜欢在实际执行后进行此操作，因为这给
	 * 函数一个机会来根据实际授予的权限调整这个 istmt。
	 */
	if (EventTriggerSupportsObjectType(fc_istmt->objtype))
		EventTriggerCollectGrant(fc_istmt);
}

/*
 * objectNamesToOids
 *
 * 将给定类型的对象名称列表转换为 Oid 列表。
 *
 * XXX：此函数不会对其查找名称的对象进行任何形式的锁定。
 * 在并发 DDL 的情况下，我们可能很容易与对象的旧版本纠缠在一起，
 * 造成 GRANT 或 REVOKE 语句失败。
 */
static List * fc_objectNamesToOids(ObjectType fc_objtype, List *fc_objnames, bool fc_is_grant)
{
	List	   *fc_objects = NIL;
	ListCell   *fc_cell;

	Assert(fc_objnames != NIL);

	switch (fc_objtype)
	{
		case OBJECT_TABLE:
		case OBJECT_SEQUENCE:
			foreach(fc_cell, fc_objnames)
			{
				RangeVar   *fc_relvar = (RangeVar *) lfirst(fc_cell);
				Oid			fc_relOid;

				fc_relOid = RangeVarGetRelid(fc_relvar, NoLock, false);
				fc_objects = lappend_oid(fc_objects, fc_relOid);
			}
			break;
		case OBJECT_DATABASE:
			foreach(fc_cell, fc_objnames)
			{
				char	   *fc_dbname = strVal(lfirst(fc_cell));
				Oid			fc_dbid;

				fc_dbid = get_database_oid(fc_dbname, false);
				fc_objects = lappend_oid(fc_objects, fc_dbid);
			}
			break;
		case OBJECT_DOMAIN:
		case OBJECT_TYPE:
			foreach(fc_cell, fc_objnames)
			{
				List	   *fc_typname = (List *) lfirst(fc_cell);
				Oid			fc_oid;

				fc_oid = typenameTypeId(NULL, makeTypeNameFromNameList(fc_typname));
				fc_objects = lappend_oid(fc_objects, fc_oid);
			}
			break;
		case OBJECT_FUNCTION:
			foreach(fc_cell, fc_objnames)
			{
				ObjectWithArgs *fc_func = (ObjectWithArgs *) lfirst(fc_cell);
				Oid			fc_funcid;

				fc_funcid = LookupFuncWithArgs(OBJECT_FUNCTION, fc_func, false);
				fc_objects = lappend_oid(fc_objects, fc_funcid);
			}
			break;
		case OBJECT_LANGUAGE:
			foreach(fc_cell, fc_objnames)
			{
				char	   *fc_langname = strVal(lfirst(fc_cell));
				Oid			fc_oid;

				fc_oid = get_language_oid(fc_langname, false);
				fc_objects = lappend_oid(fc_objects, fc_oid);
			}
			break;
		case OBJECT_LARGEOBJECT:
			foreach(fc_cell, fc_objnames)
			{
				Oid			fc_lobjOid = oidparse(lfirst(fc_cell));

				if (!LargeObjectExists(fc_lobjOid))
					ereport(ERROR,
							(errcode(ERRCODE_UNDEFINED_OBJECT),
							 errmsg("large object %u does not exist",
									fc_lobjOid)));

				fc_objects = lappend_oid(fc_objects, fc_lobjOid);
			}
			break;
		case OBJECT_SCHEMA:
			foreach(fc_cell, fc_objnames)
			{
				char	   *fc_nspname = strVal(lfirst(fc_cell));
				Oid			fc_oid;

				fc_oid = get_namespace_oid(fc_nspname, false);
				fc_objects = lappend_oid(fc_objects, fc_oid);
			}
			break;
		case OBJECT_PROCEDURE:
			foreach(fc_cell, fc_objnames)
			{
				ObjectWithArgs *fc_func = (ObjectWithArgs *) lfirst(fc_cell);
				Oid			fc_procid;

				fc_procid = LookupFuncWithArgs(OBJECT_PROCEDURE, fc_func, false);
				fc_objects = lappend_oid(fc_objects, fc_procid);
			}
			break;
		case OBJECT_ROUTINE:
			foreach(fc_cell, fc_objnames)
			{
				ObjectWithArgs *fc_func = (ObjectWithArgs *) lfirst(fc_cell);
				Oid			fc_routid;

				fc_routid = LookupFuncWithArgs(OBJECT_ROUTINE, fc_func, false);
				fc_objects = lappend_oid(fc_objects, fc_routid);
			}
			break;
		case OBJECT_TABLESPACE:
			foreach(fc_cell, fc_objnames)
			{
				char	   *fc_spcname = strVal(lfirst(fc_cell));
				Oid			fc_spcoid;

				fc_spcoid = get_tablespace_oid(fc_spcname, false);
				fc_objects = lappend_oid(fc_objects, fc_spcoid);
			}
			break;
		case OBJECT_FDW:
			foreach(fc_cell, fc_objnames)
			{
				char	   *fc_fdwname = strVal(lfirst(fc_cell));
				Oid			fc_fdwid = get_foreign_data_wrapper_oid(fc_fdwname, false);

				fc_objects = lappend_oid(fc_objects, fc_fdwid);
			}
			break;
		case OBJECT_FOREIGN_SERVER:
			foreach(fc_cell, fc_objnames)
			{
				char	   *fc_srvname = strVal(lfirst(fc_cell));
				Oid			fc_srvid = get_foreign_server_oid(fc_srvname, false);

				fc_objects = lappend_oid(fc_objects, fc_srvid);
			}
			break;
		case OBJECT_PARAMETER_ACL:
			foreach(fc_cell, fc_objnames)
			{
				/*
				 * 在此代码中，我们通过其在 pg_parameter_acl 中的
				 * 条目 OID 表示 GUC，如果它尚不存在，我们必须
				 * 在此处制造之。 （这确实是一种黑客方式，但它避免了
				 * 混淆所有期望使用 OID 作为对象身份的 GRANT/REVOKE
				 * 基础设施。）然而，如果这是 REVOKE，我们可以
				 * 只是忽略任何没有此类条目的 GUC，因为它们
				 * 不需要移除任何权限。
				 */
				char	   *fc_parameter = strVal(lfirst(fc_cell));
				Oid			fc_parameterId = ParameterAclLookup(fc_parameter, true);

				if (!OidIsValid(fc_parameterId) && fc_is_grant)
				{
					fc_parameterId = ParameterAclCreate(fc_parameter);

					/*
					 * 防止在处理重复对象时出错，并使此
					 * 新条目可见，以便 ExecGrant_Parameter
					 * 可以更新。
					 */
					CommandCounterIncrement();
				}
				if (OidIsValid(fc_parameterId))
					fc_objects = lappend_oid(fc_objects, fc_parameterId);
			}
			break;
		default:
			elog(ERROR, "unrecognized GrantStmt.objtype: %d",
				 (int) fc_objtype);
	}

	return fc_objects;
}

/*
 * objectsInSchemaToOids
 *
 * 查找指定模式中所有给定类型的对象，并生成它们的 Oids 列表。
 * 我们检查模式上的 USAGE 权限，但此处对单个对象没有权限检查。
 */
static List * fc_objectsInSchemaToOids(ObjectType fc_objtype, List *fc_nspnames)
{
	List	   *fc_objects = NIL;
	ListCell   *fc_cell;

	foreach(fc_cell, fc_nspnames)
	{
		char	   *fc_nspname = strVal(lfirst(fc_cell));
		Oid			fc_namespaceId;
		List	   *fc_objs;

		fc_namespaceId = LookupExplicitNamespace(fc_nspname, false);

		switch (fc_objtype)
		{
			case OBJECT_TABLE:
				fc_objs = fc_getRelationsInNamespace(fc_namespaceId, RELKIND_RELATION);
				fc_objects = list_concat(fc_objects, fc_objs);
				fc_objs = fc_getRelationsInNamespace(fc_namespaceId, RELKIND_VIEW);
				fc_objects = list_concat(fc_objects, fc_objs);
				fc_objs = fc_getRelationsInNamespace(fc_namespaceId, RELKIND_MATVIEW);
				fc_objects = list_concat(fc_objects, fc_objs);
				fc_objs = fc_getRelationsInNamespace(fc_namespaceId, RELKIND_FOREIGN_TABLE);
				fc_objects = list_concat(fc_objects, fc_objs);
				fc_objs = fc_getRelationsInNamespace(fc_namespaceId, RELKIND_PARTITIONED_TABLE);
				fc_objects = list_concat(fc_objects, fc_objs);
				break;
			case OBJECT_SEQUENCE:
				fc_objs = fc_getRelationsInNamespace(fc_namespaceId, RELKIND_SEQUENCE);
				fc_objects = list_concat(fc_objects, fc_objs);
				break;
			case OBJECT_FUNCTION:
			case OBJECT_PROCEDURE:
			case OBJECT_ROUTINE:
				{
					ScanKeyData fc_key[2];
					int			fc_keycount;
					Relation	fc_rel;
					TableScanDesc fc_scan;
					HeapTuple	fc_tuple;

					fc_keycount = 0;
					ScanKeyInit(&fc_key[fc_keycount++],
								Anum_pg_proc_pronamespace,
								BTEqualStrategyNumber, F_OIDEQ,
								ObjectIdGetDatum(fc_namespaceId));

					if (fc_objtype == OBJECT_FUNCTION)
						/* 包括聚合和窗口函数 */
						ScanKeyInit(&fc_key[fc_keycount++],
									Anum_pg_proc_prokind,
									BTEqualStrategyNumber, F_CHARNE,
									CharGetDatum(PROKIND_PROCEDURE));
					else if (fc_objtype == OBJECT_PROCEDURE)
						ScanKeyInit(&fc_key[fc_keycount++],
									Anum_pg_proc_prokind,
									BTEqualStrategyNumber, F_CHAREQ,
									CharGetDatum(PROKIND_PROCEDURE));

					fc_rel = table_open(ProcedureRelationId, AccessShareLock);
					fc_scan = table_beginscan_catalog(fc_rel, fc_keycount, fc_key);

					while ((fc_tuple = heap_getnext(fc_scan, ForwardScanDirection)) != NULL)
					{
						Oid			fc_oid = ((Form_pg_proc) GETSTRUCT(fc_tuple))->oid;

						fc_objects = lappend_oid(fc_objects, fc_oid);
					}

					table_endscan(fc_scan);
					table_close(fc_rel, AccessShareLock);
				}
				break;
			default:
				/* 不应该发生 */
				elog(ERROR, "unrecognized GrantStmt.objtype: %d",
					 (int) fc_objtype);
		}
	}

	return fc_objects;
}

/*
 * getRelationsInNamespace
 *
 * 返回指定命名空间中按关系类型过滤的关系的 Oid 列表。
 */
static List * fc_getRelationsInNamespace(Oid fc_namespaceId, char fc_relkind)
{
	List	   *fc_relations = NIL;
	ScanKeyData fc_key[2];
	Relation	fc_rel;
	TableScanDesc fc_scan;
	HeapTuple	fc_tuple;

	ScanKeyInit(&fc_key[0],
				Anum_pg_class_relnamespace,
				BTEqualStrategyNumber, F_OIDEQ,
				ObjectIdGetDatum(fc_namespaceId));
	ScanKeyInit(&fc_key[1],
				Anum_pg_class_relkind,
				BTEqualStrategyNumber, F_CHAREQ,
				CharGetDatum(fc_relkind));

	fc_rel = table_open(RelationRelationId, AccessShareLock);
	fc_scan = table_beginscan_catalog(fc_rel, 2, fc_key);

	while ((fc_tuple = heap_getnext(fc_scan, ForwardScanDirection)) != NULL)
	{
		Oid			fc_oid = ((Form_pg_class) GETSTRUCT(fc_tuple))->oid;

		fc_relations = lappend_oid(fc_relations, fc_oid);
	}

	table_endscan(fc_scan);
	table_close(fc_rel, AccessShareLock);

	return fc_relations;
}


/*
 * ALTER DEFAULT PRIVILEGES 语句
 */
void ExecAlterDefaultPrivilegesStmt(ParseState *fc_pstate, AlterDefaultPrivilegesStmt *fc_stmt)
{
	GrantStmt  *fc_action = fc_stmt->action;
	InternalDefaultACL fc_iacls;
	ListCell   *fc_cell;
	List	   *fc_rolespecs = NIL;
	List	   *fc_nspnames = NIL;
	DefElem    *fc_drolespecs = NULL;
	DefElem    *fc_dnspnames = NULL;
	AclMode		fc_all_privileges;
	const char *fc_errormsg;

	/* 拆解语句的 "options" 部分 */
	foreach(fc_cell, fc_stmt->options)
	{
		DefElem    *fc_defel = (DefElem *) lfirst(fc_cell);

		if (strcmp(fc_defel->defname, "schemas") == 0)
		{
			if (fc_dnspnames)
				errorConflictingDefElem(fc_defel, fc_pstate);
			fc_dnspnames = fc_defel;
		}
		else if (strcmp(fc_defel->defname, "roles") == 0)
		{
			if (fc_drolespecs)
				errorConflictingDefElem(fc_defel, fc_pstate);
			fc_drolespecs = fc_defel;
		}
		else
			elog(ERROR, "option \"%s\" not recognized", fc_defel->defname);
	}

	if (fc_dnspnames)
		fc_nspnames = (List *) fc_dnspnames->arg;
	if (fc_drolespecs)
		fc_rolespecs = (List *) fc_drolespecs->arg;

	/* 准备语句的 InternalDefaultACL 表示 */
	/* roleid 将在下面填充 */
	/* nspid 将在 SetDefaultACLsInSchemas 中填充 */
	fc_iacls.is_grant = fc_action->is_grant;
	fc_iacls.objtype = fc_action->objtype;
	/* all_privs 将在下面填充 */
	/* privileges 将在下面填充 */
	fc_iacls.grantees = NIL;		/* 在下面填充 */
	fc_iacls.grant_option = fc_action->grant_option;
	fc_iacls.behavior = fc_action->behavior;

	/*
	 * 将 RoleSpec 列表转换为 Oid 列表。请注意，此时如果合适，我们将在列表中插入 ACL_ID_PUBLIC，这样下游就不需要额外的工作来支持这种情况。
	 */
	foreach(fc_cell, fc_action->grantees)
	{
		RoleSpec   *fc_grantee = (RoleSpec *) lfirst(fc_cell);
		Oid			fc_grantee_uid;

		switch (fc_grantee->roletype)
		{
			case ROLESPEC_PUBLIC:
				fc_grantee_uid = ACL_ID_PUBLIC;
				break;
			default:
				fc_grantee_uid = get_rolespec_oid(fc_grantee, false);
				break;
		}
		fc_iacls.grantees = lappend_oid(fc_iacls.grantees, fc_grantee_uid);
	}

	/*
	 * 将 action->privileges（一个权限字符串的列表）转换为
	 * AclMode 位掩码。
	 */
	switch (fc_action->objtype)
	{
		case OBJECT_TABLE:
			fc_all_privileges = ACL_ALL_RIGHTS_RELATION;
			fc_errormsg = gettext_noop("invalid privilege type %s for relation");
			break;
		case OBJECT_SEQUENCE:
			fc_all_privileges = ACL_ALL_RIGHTS_SEQUENCE;
			fc_errormsg = gettext_noop("invalid privilege type %s for sequence");
			break;
		case OBJECT_FUNCTION:
			fc_all_privileges = ACL_ALL_RIGHTS_FUNCTION;
			fc_errormsg = gettext_noop("invalid privilege type %s for function");
			break;
		case OBJECT_PROCEDURE:
			fc_all_privileges = ACL_ALL_RIGHTS_FUNCTION;
			fc_errormsg = gettext_noop("invalid privilege type %s for procedure");
			break;
		case OBJECT_ROUTINE:
			fc_all_privileges = ACL_ALL_RIGHTS_FUNCTION;
			fc_errormsg = gettext_noop("invalid privilege type %s for routine");
			break;
		case OBJECT_TYPE:
			fc_all_privileges = ACL_ALL_RIGHTS_TYPE;
			fc_errormsg = gettext_noop("invalid privilege type %s for type");
			break;
		case OBJECT_SCHEMA:
			fc_all_privileges = ACL_ALL_RIGHTS_SCHEMA;
			fc_errormsg = gettext_noop("invalid privilege type %s for schema");
			break;
		default:
			elog(ERROR, "unrecognized GrantStmt.objtype: %d",
				 (int) fc_action->objtype);
			/* 保持编译器安静 */
			fc_all_privileges = ACL_NO_RIGHTS;
			fc_errormsg = NULL;
	}

	if (fc_action->privileges == NIL)
	{
		fc_iacls.all_privs = true;

		/*
		 * 将根据对象类型由内部例程转换为 ACL_ALL_RIGHTS_*。
		 */
		fc_iacls.privileges = ACL_NO_RIGHTS;
	}
	else
	{
		fc_iacls.all_privs = false;
		fc_iacls.privileges = ACL_NO_RIGHTS;

		foreach(fc_cell, fc_action->privileges)
		{
			AccessPriv *fc_privnode = (AccessPriv *) lfirst(fc_cell);
			AclMode		fc_priv;

			if (fc_privnode->cols)
				ereport(ERROR,
						(errcode(ERRCODE_INVALID_GRANT_OPERATION),
						 errmsg("default privileges cannot be set for columns")));

			if (fc_privnode->priv_name == NULL)	/* 解析器错误？ */
				elog(ERROR, "AccessPriv node must specify privilege");
			fc_priv = fc_string_to_privilege(fc_privnode->priv_name);

			if (fc_priv & ~((AclMode) fc_all_privileges))
				ereport(ERROR,
						(errcode(ERRCODE_INVALID_GRANT_OPERATION),
						 errmsg(fc_errormsg, fc_privilege_to_string(fc_priv))));

			fc_iacls.privileges |= fc_priv;
		}
	}

	if (fc_rolespecs == NIL)
	{
		/* 设置我自己的权限 */
		fc_iacls.roleid = GetUserId();

		fc_SetDefaultACLsInSchemas(&fc_iacls, fc_nspnames);
	}
	else
	{
		/* 查询角色 OIDs 并进行权限检查 */
		ListCell   *fc_rolecell;

		foreach(fc_rolecell, fc_rolespecs)
		{
			RoleSpec   *fc_rolespec = lfirst(fc_rolecell);

			fc_iacls.roleid = get_rolespec_oid(fc_rolespec, false);

			/*
			 * 我们坚持调用用户是每个目标角色的成员。如果
			 * 他有资格，他可以通过 SET ROLE 成为该角色，
			 * 因此 FOR ROLE 只是语法上的方便，并没有赋予任何
			 * 特殊权限。
			 */
			check_is_member_of_role(GetUserId(), fc_iacls.roleid);

			fc_SetDefaultACLsInSchemas(&fc_iacls, fc_nspnames);
		}
	}
}

/*
 * 处理一组目标模式的 ALTER DEFAULT PRIVILEGES。
 *
 * 除 nspid 外，*iacls 的所有字段已经填充。
 */
static void fc_SetDefaultACLsInSchemas(InternalDefaultACL *fc_iacls, List *fc_nspnames)
{
	if (fc_nspnames == NIL)
	{
		/* 如果未指定模式，则设置数据库级别的权限 */
		fc_iacls->nspid = InvalidOid;

		fc_SetDefaultACL(fc_iacls);
	}
	else
	{
		/* 查询模式 OIDs 并为每个设置权限 */
		ListCell   *fc_nspcell;

		foreach(fc_nspcell, fc_nspnames)
		{
			char	   *fc_nspname = strVal(lfirst(fc_nspcell));

			fc_iacls->nspid = get_namespace_oid(fc_nspname, false);

			/*
			 * 我们过去坚持要求目标角色在模式上具有 CREATE 权限，
			 * 因为没有这个权限，它将无法创建适用于这些默认权限的对象。
			 * 然而，这一检查被证明比帮助更混乱，
			 * 也导致某些数据库状态不可转储/恢复，因为撤销 CREATE
			 * 不会导致模式的默认权限消失。因此，现在，我们只是允许
			 * ALTER；如果用户缺少 CREATE，他将在尝试创建对象时发现。
			 */

			fc_SetDefaultACL(fc_iacls);
		}
	}
}


/*
 * 创建或更新 pg_default_acl 条目
 */
static void fc_SetDefaultACL(InternalDefaultACL *fc_iacls)
{
	AclMode		fc_this_privileges = fc_iacls->privileges;
	char		fc_objtype;
	Relation	fc_rel;
	HeapTuple	fc_tuple;
	bool		fc_isNew;
	Acl		   *fc_def_acl;
	Acl		   *fc_old_acl;
	Acl		   *fc_new_acl;
	HeapTuple	fc_newtuple;
	Datum		fc_values[Natts_pg_default_acl];
	bool		fc_nulls[Natts_pg_default_acl];
	bool		fc_replaces[Natts_pg_default_acl];
	int			fc_noldmembers;
	int			fc_nnewmembers;
	Oid		   *fc_oldmembers;
	Oid		   *fc_newmembers;

	fc_rel = table_open(DefaultAclRelationId, RowExclusiveLock);

	/*
	 * 全局条目的默认值是特定对象类型的硬编码默认 ACL。
	 * 非全局条目的默认值是空 ACL。必须这样做，因为
	 * 全局条目替换硬编码的默认值，而其他条目则被添加。
	 */
	if (!OidIsValid(fc_iacls->nspid))
		fc_def_acl = acldefault(fc_iacls->objtype, fc_iacls->roleid);
	else
		fc_def_acl = make_empty_acl();

	/*
	 * 将 ACL 对象类型转换为 pg_default_acl 对象类型并处理
	 * all_privs 选项。
	 */
	switch (fc_iacls->objtype)
	{
		case OBJECT_TABLE:
			fc_objtype = DEFACLOBJ_RELATION;
			if (fc_iacls->all_privs && fc_this_privileges == ACL_NO_RIGHTS)
				fc_this_privileges = ACL_ALL_RIGHTS_RELATION;
			break;

		case OBJECT_SEQUENCE:
			fc_objtype = DEFACLOBJ_SEQUENCE;
			if (fc_iacls->all_privs && fc_this_privileges == ACL_NO_RIGHTS)
				fc_this_privileges = ACL_ALL_RIGHTS_SEQUENCE;
			break;

		case OBJECT_FUNCTION:
			fc_objtype = DEFACLOBJ_FUNCTION;
			if (fc_iacls->all_privs && fc_this_privileges == ACL_NO_RIGHTS)
				fc_this_privileges = ACL_ALL_RIGHTS_FUNCTION;
			break;

		case OBJECT_TYPE:
			fc_objtype = DEFACLOBJ_TYPE;
			if (fc_iacls->all_privs && fc_this_privileges == ACL_NO_RIGHTS)
				fc_this_privileges = ACL_ALL_RIGHTS_TYPE;
			break;

		case OBJECT_SCHEMA:
			if (OidIsValid(fc_iacls->nspid))
				ereport(ERROR,
						(errcode(ERRCODE_INVALID_GRANT_OPERATION),
						 errmsg("cannot use IN SCHEMA clause when using GRANT/REVOKE ON SCHEMAS")));
			fc_objtype = DEFACLOBJ_NAMESPACE;
			if (fc_iacls->all_privs && fc_this_privileges == ACL_NO_RIGHTS)
				fc_this_privileges = ACL_ALL_RIGHTS_SCHEMA;
			break;

		default:
			elog(ERROR, "unrecognized objtype: %d",
				 (int) fc_iacls->objtype);
			fc_objtype = 0;		/* 保持编译器安静 */
			break;
	}

	/* 在目录中搜索此对象类型的现有行 */
	fc_tuple = SearchSysCache3(DEFACLROLENSPOBJ,
							ObjectIdGetDatum(fc_iacls->roleid),
							ObjectIdGetDatum(fc_iacls->nspid),
							CharGetDatum(fc_objtype));

	if (HeapTupleIsValid(fc_tuple))
	{
		Datum		fc_aclDatum;
		bool		fc_isNull;

		fc_aclDatum = SysCacheGetAttr(DEFACLROLENSPOBJ, fc_tuple,
								   Anum_pg_default_acl_defaclacl,
								   &fc_isNull);
		if (!fc_isNull)
			fc_old_acl = DatumGetAclPCopy(fc_aclDatum);
		else
			fc_old_acl = NULL;		/* 这个情况不应该发生，可能会发生 */
		fc_isNew = false;
	}
	else
	{
		fc_old_acl = NULL;
		fc_isNew = true;
	}

	if (fc_old_acl != NULL)
	{
		/*  
		 * 我们需要旧的和新的 ACL 的成员，以便我们能够更正共享的依赖信息。
		 * 在 merge_acl_with_grant 丢弃 old_acl 之前收集数据。
		 */
		fc_noldmembers = aclmembers(fc_old_acl, &fc_oldmembers);
	}
	else
	{
		/* 如果没有或为空条目，使用默认的 ACL 值开始 */
		fc_old_acl = aclcopy(fc_def_acl);
		/* 根据目录没有旧的成员角色 */
		fc_noldmembers = 0;
		fc_oldmembers = NULL;
	}

	/*  
	 * 生成新的 ACL。 权限授予者总是与目标角色相同。
	 */
	fc_new_acl = fc_merge_acl_with_grant(fc_old_acl,
								   fc_iacls->is_grant,
								   fc_iacls->grant_option,
								   fc_iacls->behavior,
								   fc_iacls->grantees,
								   fc_this_privileges,
								   fc_iacls->roleid,
								   fc_iacls->roleid);

	/*  
	 * 如果结果与默认值相同，我们不需要显式的 pg_default_acl 条目，
	 * 如果存在，则应实际删除该条目。 必须对两个数组进行排序以进行正确比较。
	 */
	aclitemsort(fc_new_acl);
	aclitemsort(fc_def_acl);
	if (aclequal(fc_new_acl, fc_def_acl))
	{
		/* 删除旧条目，如果确实存在一个 */
		if (!fc_isNew)
		{
			ObjectAddress fc_myself;

			/*  
			 * 依赖机制将负责删除所有关联的依赖条目。 
			 * 我们使用 DROP_RESTRICT，因为不应该有任何依赖于此条目的内容。
			 */
			fc_myself.classId = DefaultAclRelationId;
			fc_myself.objectId = ((Form_pg_default_acl) GETSTRUCT(fc_tuple))->oid;
			fc_myself.objectSubId = 0;

			performDeletion(&fc_myself, DROP_RESTRICT, 0);
		}
	}
	else
	{
		Oid			fc_defAclOid;

		/* 准备插入或更新 pg_default_acl 条目 */
		MemSet(fc_values, 0, sizeof(fc_values));
		MemSet(fc_nulls, false, sizeof(fc_nulls));
		MemSet(fc_replaces, false, sizeof(fc_replaces));

		if (fc_isNew)
		{
			/* 插入新条目 */
			fc_defAclOid = GetNewOidWithIndex(fc_rel, DefaultAclOidIndexId,
										   Anum_pg_default_acl_oid);
			fc_values[Anum_pg_default_acl_oid - 1] = ObjectIdGetDatum(fc_defAclOid);
			fc_values[Anum_pg_default_acl_defaclrole - 1] = ObjectIdGetDatum(fc_iacls->roleid);
			fc_values[Anum_pg_default_acl_defaclnamespace - 1] = ObjectIdGetDatum(fc_iacls->nspid);
			fc_values[Anum_pg_default_acl_defaclobjtype - 1] = CharGetDatum(fc_objtype);
			fc_values[Anum_pg_default_acl_defaclacl - 1] = PointerGetDatum(fc_new_acl);

			fc_newtuple = heap_form_tuple(RelationGetDescr(fc_rel), fc_values, fc_nulls);
			CatalogTupleInsert(fc_rel, fc_newtuple);
		}
		else
		{
			fc_defAclOid = ((Form_pg_default_acl) GETSTRUCT(fc_tuple))->oid;

			/* 更新现有条目 */
			fc_values[Anum_pg_default_acl_defaclacl - 1] = PointerGetDatum(fc_new_acl);
			fc_replaces[Anum_pg_default_acl_defaclacl - 1] = true;

			fc_newtuple = heap_modify_tuple(fc_tuple, RelationGetDescr(fc_rel),
										 fc_values, fc_nulls, fc_replaces);
			CatalogTupleUpdate(fc_rel, &fc_newtuple->t_self, fc_newtuple);
		}

		/* 这些依赖在更新中不会改变 */
		if (fc_isNew)
		{
			/* 对角色的依赖 */
			recordDependencyOnOwner(DefaultAclRelationId, fc_defAclOid,
									fc_iacls->roleid);

			/* 对命名空间的依赖 */
			if (OidIsValid(fc_iacls->nspid))
			{
				ObjectAddress fc_myself,
							fc_referenced;

				fc_myself.classId = DefaultAclRelationId;
				fc_myself.objectId = fc_defAclOid;
				fc_myself.objectSubId = 0;

				fc_referenced.classId = NamespaceRelationId;
				fc_referenced.objectId = fc_iacls->nspid;
				fc_referenced.objectSubId = 0;

				recordDependencyOn(&fc_myself, &fc_referenced, DEPENDENCY_AUTO);
			}
		}

		/*  
		 * 更新共享依赖 ACL 信息
		 */
		fc_nnewmembers = aclmembers(fc_new_acl, &fc_newmembers);

		updateAclDependencies(DefaultAclRelationId,
							  fc_defAclOid, 0,
							  fc_iacls->roleid,
							  fc_noldmembers, fc_oldmembers,
							  fc_nnewmembers, fc_newmembers);

		if (fc_isNew)
			InvokeObjectPostCreateHook(DefaultAclRelationId, fc_defAclOid, 0);
		else
			InvokeObjectPostAlterHook(DefaultAclRelationId, fc_defAclOid, 0);
	}

	if (HeapTupleIsValid(fc_tuple))
		ReleaseSysCache(fc_tuple);

	table_close(fc_rel, RowExclusiveLock);

	/* 防止处理重复对象时出错 */
	CommandCounterIncrement();
}


/*  
 * RemoveRoleFromObjectACL
 *
 * 用于 shdepDropOwned 从 ACL 中删除角色的提及
 */
void RemoveRoleFromObjectACL(Oid fc_roleid, Oid fc_classid, Oid fc_objid)
{
	if (fc_classid == DefaultAclRelationId)
	{
		InternalDefaultACL fc_iacls;
		Form_pg_default_acl pg_default_acl_tuple;
		Relation	fc_rel;
		ScanKeyData fc_skey[1];
		SysScanDesc fc_scan;
		HeapTuple	fc_tuple;

		/* 首先获取 SetDefaultACL 需要的信息 */
		fc_rel = table_open(DefaultAclRelationId, AccessShareLock);

		ScanKeyInit(&fc_skey[0],
					Anum_pg_default_acl_oid,
					BTEqualStrategyNumber, F_OIDEQ,
					ObjectIdGetDatum(fc_objid));

		fc_scan = systable_beginscan(fc_rel, DefaultAclOidIndexId, true,
								  NULL, 1, fc_skey);

		fc_tuple = systable_getnext(fc_scan);

		if (!HeapTupleIsValid(fc_tuple))
			elog(ERROR, "could not find tuple for default ACL %u", fc_objid);

		pg_default_acl_tuple = (Form_pg_default_acl) GETSTRUCT(fc_tuple);

		fc_iacls.roleid = pg_default_acl_tuple->defaclrole;
		fc_iacls.nspid = pg_default_acl_tuple->defaclnamespace;

		switch (pg_default_acl_tuple->defaclobjtype)
		{
			case DEFACLOBJ_RELATION:
				fc_iacls.objtype = OBJECT_TABLE;
				break;
			case DEFACLOBJ_SEQUENCE:
				fc_iacls.objtype = OBJECT_SEQUENCE;
				break;
			case DEFACLOBJ_FUNCTION:
				fc_iacls.objtype = OBJECT_FUNCTION;
				break;
			case DEFACLOBJ_TYPE:
				fc_iacls.objtype = OBJECT_TYPE;
				break;
			case DEFACLOBJ_NAMESPACE:
				fc_iacls.objtype = OBJECT_SCHEMA;
				break;
			default:
				/* 不应该到达这里 */
				elog(ERROR, "unexpected default ACL type: %d",
					 (int) pg_default_acl_tuple->defaclobjtype);
				break;
		}

		systable_endscan(fc_scan);
		table_close(fc_rel, AccessShareLock);

		fc_iacls.is_grant = false;
		fc_iacls.all_privs = true;
		fc_iacls.privileges = ACL_NO_RIGHTS;
		fc_iacls.grantees = list_make1_oid(fc_roleid);
		fc_iacls.grant_option = false;
		fc_iacls.behavior = DROP_CASCADE;

		/* 执行它 */
		fc_SetDefaultACL(&fc_iacls);
	}
	else
	{
		InternalGrant fc_istmt;

		switch (fc_classid)
		{
			case RelationRelationId:
				/* 使用 TABLE 来处理序列是可以的 */
				fc_istmt.objtype = OBJECT_TABLE;
				break;
			case DatabaseRelationId:
				fc_istmt.objtype = OBJECT_DATABASE;
				break;
			case TypeRelationId:
				fc_istmt.objtype = OBJECT_TYPE;
				break;
			case ProcedureRelationId:
				fc_istmt.objtype = OBJECT_ROUTINE;
				break;
			case LanguageRelationId:
				fc_istmt.objtype = OBJECT_LANGUAGE;
				break;
			case LargeObjectRelationId:
				fc_istmt.objtype = OBJECT_LARGEOBJECT;
				break;
			case NamespaceRelationId:
				fc_istmt.objtype = OBJECT_SCHEMA;
				break;
			case TableSpaceRelationId:
				fc_istmt.objtype = OBJECT_TABLESPACE;
				break;
			case ForeignServerRelationId:
				fc_istmt.objtype = OBJECT_FOREIGN_SERVER;
				break;
			case ForeignDataWrapperRelationId:
				fc_istmt.objtype = OBJECT_FDW;
				break;
			case ParameterAclRelationId:
				fc_istmt.objtype = OBJECT_PARAMETER_ACL;
				break;
			default:
				elog(ERROR, "unexpected object class %u", fc_classid);
				break;
		}
		fc_istmt.is_grant = false;
		fc_istmt.objects = list_make1_oid(fc_objid);
		fc_istmt.all_privs = true;
		fc_istmt.privileges = ACL_NO_RIGHTS;
		fc_istmt.col_privs = NIL;
		fc_istmt.grantees = list_make1_oid(fc_roleid);
		fc_istmt.grant_option = false;
		fc_istmt.behavior = DROP_CASCADE;

		fc_ExecGrantStmt_oids(&fc_istmt);
	}
}


/*  
 * expand_col_privileges
 *
 * 将指定的特权 OR 运营到每个指定属性的列数组条目中。
 * 每列数组从 FirstLowInvalidHeapAttributeNumber 开始索引，
 * 直到关系的最后一个属性。
 */
static void fc_expand_col_privileges(List *fc_colnames, Oid fc_table_oid,
					  AclMode fc_this_privileges,
					  AclMode *fc_col_privileges,
					  int fc_num_col_privileges)
{
	ListCell   *fc_cell;

	foreach(fc_cell, fc_colnames)
	{
		char	   *fc_colname = strVal(lfirst(fc_cell));
		AttrNumber	fc_attnum;

		fc_attnum = get_attnum(fc_table_oid, fc_colname);
		if (fc_attnum == InvalidAttrNumber)
			ereport(ERROR,
					(errcode(ERRCODE_UNDEFINED_COLUMN),
					 errmsg("column \"%s\" of relation \"%s\" does not exist",
							fc_colname, get_rel_name(fc_table_oid))));
		fc_attnum -= FirstLowInvalidHeapAttributeNumber;
		if (fc_attnum <= 0 || fc_attnum >= fc_num_col_privileges)
			elog(ERROR, "column number out of range");	/* 安全检查 */
		fc_col_privileges[fc_attnum] |= fc_this_privileges;
	}
}

/*  
 * expand_all_col_privileges
 *
 * 将指定的特权 OR 运营到每个关系的有效属性的列数组条目中。
 * 每列数组从 FirstLowInvalidHeapAttributeNumber 开始索引，
 * 直到关系的最后一个属性。
 */
static void fc_expand_all_col_privileges(Oid fc_table_oid, Form_pg_class fc_classForm,
						  AclMode fc_this_privileges,
						  AclMode *fc_col_privileges,
						  int fc_num_col_privileges)
{
	AttrNumber	fc_curr_att;

	Assert(fc_classForm->relnatts - FirstLowInvalidHeapAttributeNumber < fc_num_col_privileges);
	for (fc_curr_att = FirstLowInvalidHeapAttributeNumber + 1;
		 fc_curr_att <= fc_classForm->relnatts;
		 fc_curr_att++)
	{
		HeapTuple	fc_attTuple;
		bool		fc_isdropped;

		if (fc_curr_att == InvalidAttrNumber)
			continue;

		/* 视图根本没有任何系统列 */
		if (fc_classForm->relkind == RELKIND_VIEW && fc_curr_att < 0)
			continue;

		fc_attTuple = SearchSysCache2(ATTNUM,
								   ObjectIdGetDatum(fc_table_oid),
								   Int16GetDatum(fc_curr_att));
		if (!HeapTupleIsValid(fc_attTuple))
			elog(ERROR, "cache lookup failed for attribute %d of relation %u",
				 fc_curr_att, fc_table_oid);

		fc_isdropped = ((Form_pg_attribute) GETSTRUCT(fc_attTuple))->attisdropped;

		ReleaseSysCache(fc_attTuple);

		/* 忽略已删除的列 */
		if (fc_isdropped)
			continue;

		fc_col_privileges[fc_curr_att - FirstLowInvalidHeapAttributeNumber] |= fc_this_privileges;
	}
}

/*  
 *	这处理属性，但期望从
 *	ExecGrant_Relation 调用，而不是直接从 ExecuteGrantStmt。
 */
static void fc_ExecGrant_Attribute(InternalGrant *fc_istmt, Oid fc_relOid, const char *fc_relname,
					AttrNumber fc_attnum, Oid fc_ownerId, AclMode fc_col_privileges,
					Relation fc_attRelation, const Acl *fc_old_rel_acl)
{
	HeapTuple	fc_attr_tuple;
	Form_pg_attribute pg_attribute_tuple;
	Acl		   *fc_old_acl;
	Acl		   *fc_new_acl;
	Acl		   *fc_merged_acl;
	Datum		fc_aclDatum;
	bool		fc_isNull;
	Oid			fc_grantorId;
	AclMode		fc_avail_goptions;
	bool		fc_need_update;
	HeapTuple	fc_newtuple;
	Datum		fc_values[Natts_pg_attribute];
	bool		fc_nulls[Natts_pg_attribute];
	bool		fc_replaces[Natts_pg_attribute];
	int			fc_noldmembers;
	int			fc_nnewmembers;
	Oid		   *fc_oldmembers;
	Oid		   *fc_newmembers;

	fc_attr_tuple = SearchSysCache2(ATTNUM,
								 ObjectIdGetDatum(fc_relOid),
								 Int16GetDatum(fc_attnum));
	if (!HeapTupleIsValid(fc_attr_tuple))
		elog(ERROR, "cache lookup failed for attribute %d of relation %u",
			 fc_attnum, fc_relOid);
	pg_attribute_tuple = (Form_pg_attribute) GETSTRUCT(fc_attr_tuple);

	/*  
	 * 获取现有 ACL 的工作副本。如果没有 ACL，则替换为适当的默认值。
	 */
	fc_aclDatum = SysCacheGetAttr(ATTNUM, fc_attr_tuple, Anum_pg_attribute_attacl,
							   &fc_isNull);
	if (fc_isNull)
	{
		fc_old_acl = acldefault(OBJECT_COLUMN, fc_ownerId);
		/* 根据目录没有旧的成员角色 */
		fc_noldmembers = 0;
		fc_oldmembers = NULL;
	}
	else
	{
		fc_old_acl = DatumGetAclPCopy(fc_aclDatum);
		/* 获取现有 ACL 中提到的角色 */
		fc_noldmembers = aclmembers(fc_old_acl, &fc_oldmembers);
	}

	/*  
	 * 在 select_best_grantor 中，我们应该考虑现有的表级 ACL 位
	 * 以及每列的 ACL。 构建一个新的 ACL，它们的
	 * 连接。 （这比没有重复的正确合并要廉价和脏一点，但这正是我们在这里需要的。）
	 */
	fc_merged_acl = aclconcat(fc_old_rel_acl, fc_old_acl);

	/* 确定执行授予的 ID，以及可用的授予选项 */
	select_best_grantor(GetUserId(), fc_col_privileges,
						fc_merged_acl, fc_ownerId,
						&fc_grantorId, &fc_avail_goptions);

	pfree(fc_merged_acl);

	/*
	 * 限制特权到我们实际可以授予的，并发出标准要求的警告和错误消息。注意：我们不会跟踪用户是否实际使用了 ALL PRIVILEGES(columns) 语法针对每一列；我们只是通过当前是否指定了所有可能的特权来近似。如果 all_privs 标志仅决定是否发出警告，那么这似乎足够接近。
	 */
	fc_col_privileges =
		fc_restrict_and_check_grant(fc_istmt->is_grant, fc_avail_goptions,
								 (fc_col_privileges == ACL_ALL_RIGHTS_COLUMN),
								 fc_col_privileges,
								 fc_relOid, fc_grantorId, OBJECT_COLUMN,
								 fc_relname, fc_attnum,
								 NameStr(pg_attribute_tuple->attname));

	/*
	 * 生成新的 ACL。
	 */
	fc_new_acl = fc_merge_acl_with_grant(fc_old_acl, fc_istmt->is_grant,
								   fc_istmt->grant_option,
								   fc_istmt->behavior, fc_istmt->grantees,
								   fc_col_privileges, fc_grantorId,
								   fc_ownerId);

	/*
	 * 我们需要旧 ACL 和新 ACL 的成员，以便我们可以纠正
	 * 共享依赖信息。
	 */
	fc_nnewmembers = aclmembers(fc_new_acl, &fc_newmembers);

	/* 完成构建新的 ACL 值，现在插入它 */
	MemSet(fc_values, 0, sizeof(fc_values));
	MemSet(fc_nulls, false, sizeof(fc_nulls));
	MemSet(fc_replaces, false, sizeof(fc_replaces));

	/*
	 * 如果更新后的 ACL 为空，我们可以将 attacl 设置为 null，甚至可能
	 * 避免更新 pg_attribute 行。这是值得测试的，因为
	 * 对于任何关系级别的 REVOKE，我们都会多次通过这里，
	 * 即使从未有过任何列的 GRANT。注意我们假设
	 * 列的“默认”ACL状态为空。
	 */
	if (ACL_NUM(fc_new_acl) > 0)
	{
		fc_values[Anum_pg_attribute_attacl - 1] = PointerGetDatum(fc_new_acl);
		fc_need_update = true;
	}
	else
	{
		fc_nulls[Anum_pg_attribute_attacl - 1] = true;
		fc_need_update = !fc_isNull;
	}
	fc_replaces[Anum_pg_attribute_attacl - 1] = true;

	if (fc_need_update)
	{
		fc_newtuple = heap_modify_tuple(fc_attr_tuple, RelationGetDescr(fc_attRelation),
									 fc_values, fc_nulls, fc_replaces);

		CatalogTupleUpdate(fc_attRelation, &fc_newtuple->t_self, fc_newtuple);

		/* 更新扩展的初始权限 */
		fc_recordExtensionInitPriv(fc_relOid, RelationRelationId, fc_attnum,
								ACL_NUM(fc_new_acl) > 0 ? fc_new_acl : NULL);

		/* 更新共享依赖 ACL 信息 */
		updateAclDependencies(RelationRelationId, fc_relOid, fc_attnum,
							  fc_ownerId,
							  fc_noldmembers, fc_oldmembers,
							  fc_nnewmembers, fc_newmembers);
	}

	pfree(fc_new_acl);

	ReleaseSysCache(fc_attr_tuple);
}

/*
 * 这处理序列和非序列。
 */
static void fc_ExecGrant_Relation(InternalGrant *fc_istmt)
{
	Relation	fc_relation;
	Relation	fc_attRelation;
	ListCell   *fc_cell;

	fc_relation = table_open(RelationRelationId, RowExclusiveLock);
	fc_attRelation = table_open(AttributeRelationId, RowExclusiveLock);

	foreach(fc_cell, fc_istmt->objects)
	{
		Oid			fc_relOid = lfirst_oid(fc_cell);
		Datum		fc_aclDatum;
		Form_pg_class pg_class_tuple;
		bool		fc_isNull;
		AclMode		fc_this_privileges;
		AclMode    *fc_col_privileges;
		int			fc_num_col_privileges;
		bool		fc_have_col_privileges;
		Acl		   *fc_old_acl;
		Acl		   *fc_old_rel_acl;
		int			fc_noldmembers;
		Oid		   *fc_oldmembers;
		Oid			fc_ownerId;
		HeapTuple	fc_tuple;
		ListCell   *fc_cell_colprivs;

		fc_tuple = SearchSysCacheLocked1(RELOID, ObjectIdGetDatum(fc_relOid));
		if (!HeapTupleIsValid(fc_tuple))
			elog(ERROR, "cache lookup failed for relation %u", fc_relOid);
		pg_class_tuple = (Form_pg_class) GETSTRUCT(fc_tuple);

		/* 在索引上授予并不合理 */
		if (pg_class_tuple->relkind == RELKIND_INDEX ||
			pg_class_tuple->relkind == RELKIND_PARTITIONED_INDEX)
			ereport(ERROR,
					(errcode(ERRCODE_WRONG_OBJECT_TYPE),
					 errmsg("\"%s\" is an index",
							NameStr(pg_class_tuple->relname))));

		/* 复合类型也不是表 */
		if (pg_class_tuple->relkind == RELKIND_COMPOSITE_TYPE)
			ereport(ERROR,
					(errcode(ERRCODE_WRONG_OBJECT_TYPE),
					 errmsg("\"%s\" is a composite type",
							NameStr(pg_class_tuple->relname))));

		/* 在非序列上使用 GRANT SEQUENCE？ */
		if (fc_istmt->objtype == OBJECT_SEQUENCE &&
			pg_class_tuple->relkind != RELKIND_SEQUENCE)
			ereport(ERROR,
					(errcode(ERRCODE_WRONG_OBJECT_TYPE),
					 errmsg("\"%s\" is not a sequence",
							NameStr(pg_class_tuple->relname))));

		/* 根据对象类型调整默认权限 */
		if (fc_istmt->all_privs && fc_istmt->privileges == ACL_NO_RIGHTS)
		{
			if (pg_class_tuple->relkind == RELKIND_SEQUENCE)
				fc_this_privileges = ACL_ALL_RIGHTS_SEQUENCE;
			else
				fc_this_privileges = ACL_ALL_RIGHTS_RELATION;
		}
		else
			fc_this_privileges = fc_istmt->privileges;

		/*
		 * GRANT TABLE 语法可用于序列和非序列，
		 * 因此我们必须查看 relkind 以确定支持的
		 * 权限。表和序列权限的或运算已经
		 * 检查过。
		 */
		if (fc_istmt->objtype == OBJECT_TABLE)
		{
			if (pg_class_tuple->relkind == RELKIND_SEQUENCE)
			{
				/*
				 * 为了向后兼容，在使用非序列
				 * GRANT 语法时，对无效的序列权限仅发出警告。
				 */
				if (fc_this_privileges & ~((AclMode) ACL_ALL_RIGHTS_SEQUENCE))
				{
					/*
					 * 提到对象名称，因为用户需要知道
					 * 哪些操作成功。这是必要的，因为
					 * 警告允许命令继续。
					 */
					ereport(WARNING,
							(errcode(ERRCODE_INVALID_GRANT_OPERATION),
							 errmsg("sequence \"%s\" only supports USAGE, SELECT, and UPDATE privileges",
									NameStr(pg_class_tuple->relname))));
					fc_this_privileges &= (AclMode) ACL_ALL_RIGHTS_SEQUENCE;
				}
			}
			else
			{
				if (fc_this_privileges & ~((AclMode) ACL_ALL_RIGHTS_RELATION))
				{
					/*
					 * USAGE 是序列支持的唯一权限，但
					 * 非序列不支持。不要提到对象名称，
					 * 因为我们在组合的 TABLE | SEQUENCE
					 * 检查中没有提到。
					 */
					ereport(ERROR,
							(errcode(ERRCODE_INVALID_GRANT_OPERATION),
							 errmsg("invalid privilege type %s for table",
									"USAGE")));
				}
			}
		}

		/*
		 * 设置数组以累积任何需要修改的列权限位。
		 * 该数组的索引使得条目 [0]
		 * 对应于 FirstLowInvalidHeapAttributeNumber。
		 */
		fc_num_col_privileges = pg_class_tuple->relnatts - FirstLowInvalidHeapAttributeNumber + 1;
		fc_col_privileges = (AclMode *) palloc0(fc_num_col_privileges * sizeof(AclMode));
		fc_have_col_privileges = false;

		/*
		 * 如果我们正在撤销关系权限同时也是列
		 * 权限，我们必须根据 SQL 规范隐式撤销它们
		 * 从每列中。（在 GRANT 期间，我们不需要隐式添加列权限，
		 * 因为权限检查代码始终检查关系和每列权限。）
		 */
		if (!fc_istmt->is_grant &&
			(fc_this_privileges & ACL_ALL_RIGHTS_COLUMN) != 0)
		{
			fc_expand_all_col_privileges(fc_relOid, pg_class_tuple,
									  fc_this_privileges & ACL_ALL_RIGHTS_COLUMN,
									  fc_col_privileges,
									  fc_num_col_privileges);
			fc_have_col_privileges = true;
		}

		/*
		 * 获取拥有者 ID 和现有 ACL 的工作副本。如果没有 ACL，
		 * 则替换为适当的默认值。
		 */
		fc_ownerId = pg_class_tuple->relowner;
		fc_aclDatum = SysCacheGetAttr(RELOID, fc_tuple, Anum_pg_class_relacl,
								   &fc_isNull);
		if (fc_isNull)
		{
			switch (pg_class_tuple->relkind)
			{
				case RELKIND_SEQUENCE:
					fc_old_acl = acldefault(OBJECT_SEQUENCE, fc_ownerId);
					break;
				default:
					fc_old_acl = acldefault(OBJECT_TABLE, fc_ownerId);
					break;
			}
			/* 根据目录没有旧的成员角色 */
			fc_noldmembers = 0;
			fc_oldmembers = NULL;
		}
		else
		{
			fc_old_acl = DatumGetAclPCopy(fc_aclDatum);
			/* 获取现有 ACL 中提到的角色 */
			fc_noldmembers = aclmembers(fc_old_acl, &fc_oldmembers);
		}

		/* 需要原始 rel ACL 的额外副本以处理列 */
		fc_old_rel_acl = aclcopy(fc_old_acl);

		/*
		 * 处理关系级别权限（如果有指定的话）
		 */
		if (fc_this_privileges != ACL_NO_RIGHTS)
		{
			AclMode		fc_avail_goptions;
			Acl		   *fc_new_acl;
			Oid			fc_grantorId;
			HeapTuple	fc_newtuple;
			Datum		fc_values[Natts_pg_class];
			bool		fc_nulls[Natts_pg_class];
			bool		fc_replaces[Natts_pg_class];
			int			fc_nnewmembers;
			Oid		   *fc_newmembers;
			ObjectType	fc_objtype;

			/* 确定执行授予的 ID，以及可用的授予选项 */
			select_best_grantor(GetUserId(), fc_this_privileges,
								fc_old_acl, fc_ownerId,
								&fc_grantorId, &fc_avail_goptions);

			switch (pg_class_tuple->relkind)
			{
				case RELKIND_SEQUENCE:
					fc_objtype = OBJECT_SEQUENCE;
					break;
				default:
					fc_objtype = OBJECT_TABLE;
					break;
			}

			/*
			 * 将权限限制为我们实际可以授予的，并发出
			 * 标准规定的警告和错误消息。
			 */
			fc_this_privileges =
				fc_restrict_and_check_grant(fc_istmt->is_grant, fc_avail_goptions,
										 fc_istmt->all_privs, fc_this_privileges,
										 fc_relOid, fc_grantorId, fc_objtype,
										 NameStr(pg_class_tuple->relname),
										 0, NULL);

			/*
			 * 生成新的 ACL。
			 */
			fc_new_acl = fc_merge_acl_with_grant(fc_old_acl,
										   fc_istmt->is_grant,
										   fc_istmt->grant_option,
										   fc_istmt->behavior,
										   fc_istmt->grantees,
										   fc_this_privileges,
										   fc_grantorId,
										   fc_ownerId);

			/*
			 * 我们需要旧 ACL 和新 ACL 的成员，以便我们可以纠正
			 * 共享依赖信息。
			 */
			fc_nnewmembers = aclmembers(fc_new_acl, &fc_newmembers);

			/* 完成构建新的 ACL 值，现在插入它 */
			MemSet(fc_values, 0, sizeof(fc_values));
			MemSet(fc_nulls, false, sizeof(fc_nulls));
			MemSet(fc_replaces, false, sizeof(fc_replaces));

			fc_replaces[Anum_pg_class_relacl - 1] = true;
			fc_values[Anum_pg_class_relacl - 1] = PointerGetDatum(fc_new_acl);

			fc_newtuple = heap_modify_tuple(fc_tuple, RelationGetDescr(fc_relation),
										 fc_values, fc_nulls, fc_replaces);

			CatalogTupleUpdate(fc_relation, &fc_newtuple->t_self, fc_newtuple);
			UnlockTuple(fc_relation, &fc_tuple->t_self, InplaceUpdateTupleLock);

			/* 更新扩展的初始权限 */
			fc_recordExtensionInitPriv(fc_relOid, RelationRelationId, 0, fc_new_acl);

			/* 更新共享依赖 ACL 信息 */
			updateAclDependencies(RelationRelationId, fc_relOid, 0,
								  fc_ownerId,
								  fc_noldmembers, fc_oldmembers,
								  fc_nnewmembers, fc_newmembers);

			pfree(fc_new_acl);
		}
		else
			UnlockTuple(fc_relation, &fc_tuple->t_self, InplaceUpdateTupleLock);

		/*
		 * 处理列级别权限（如果有指定或隐含）。
		 * 我们首先将用户指定的列权限扩展到
		 * 数组中，然后遍历所有非空数组条目。
		 */
		foreach(fc_cell_colprivs, fc_istmt->col_privs)
		{
			AccessPriv *fc_col_privs = (AccessPriv *) lfirst(fc_cell_colprivs);

			if (fc_col_privs->priv_name == NULL)
				fc_this_privileges = ACL_ALL_RIGHTS_COLUMN;
			else
				fc_this_privileges = fc_string_to_privilege(fc_col_privs->priv_name);

			if (fc_this_privileges & ~((AclMode) ACL_ALL_RIGHTS_COLUMN))
				ereport(ERROR,
						(errcode(ERRCODE_INVALID_GRANT_OPERATION),
						 errmsg("invalid privilege type %s for column",
								fc_privilege_to_string(fc_this_privileges))));

			if (pg_class_tuple->relkind == RELKIND_SEQUENCE &&
				fc_this_privileges & ~((AclMode) ACL_SELECT))
			{
				/*
				 * 在序列上允许的唯一列权限是 SELECT。
				 * 这是警告而不是错误，因为我们对
				 * 关系级别的权限也是这样处理的。
				 */
				ereport(WARNING,
						(errcode(ERRCODE_INVALID_GRANT_OPERATION),
						 errmsg("sequence \"%s\" only supports SELECT column privileges",
								NameStr(pg_class_tuple->relname))));

				fc_this_privileges &= (AclMode) ACL_SELECT;
			}

			fc_expand_col_privileges(fc_col_privs->cols, fc_relOid,
								  fc_this_privileges,
								  fc_col_privileges,
								  fc_num_col_privileges);
			fc_have_col_privileges = true;
		}

		if (fc_have_col_privileges)
		{
			AttrNumber	fc_i;

			for (fc_i = 0; fc_i < fc_num_col_privileges; fc_i++)
			{
				if (fc_col_privileges[fc_i] == ACL_NO_RIGHTS)
					continue;
				fc_ExecGrant_Attribute(fc_istmt,
									fc_relOid,
									NameStr(pg_class_tuple->relname),
									fc_i + FirstLowInvalidHeapAttributeNumber,
									fc_ownerId,
									fc_col_privileges[fc_i],
									fc_attRelation,
									fc_old_rel_acl);
			}
		}

		pfree(fc_old_rel_acl);
		pfree(fc_col_privileges);

		ReleaseSysCache(fc_tuple);

		/* 防止处理重复对象时出错 */
		CommandCounterIncrement();
	}

	table_close(fc_attRelation, RowExclusiveLock);
	table_close(fc_relation, RowExclusiveLock);
}

static void fc_ExecGrant_Database(InternalGrant *fc_istmt)
{
	Relation	fc_relation;
	ListCell   *fc_cell;

	if (fc_istmt->all_privs && fc_istmt->privileges == ACL_NO_RIGHTS)
		fc_istmt->privileges = ACL_ALL_RIGHTS_DATABASE;

	fc_relation = table_open(DatabaseRelationId, RowExclusiveLock);

	foreach(fc_cell, fc_istmt->objects)
	{
		Oid			fc_datId = lfirst_oid(fc_cell);
		Form_pg_database pg_database_tuple;
		Datum		fc_aclDatum;
		bool		fc_isNull;
		AclMode		fc_avail_goptions;
		AclMode		fc_this_privileges;
		Acl		   *fc_old_acl;
		Acl		   *fc_new_acl;
		Oid			fc_grantorId;
		Oid			fc_ownerId;
		HeapTuple	fc_newtuple;
		Datum		fc_values[Natts_pg_database];
		bool		fc_nulls[Natts_pg_database];
		bool		fc_replaces[Natts_pg_database];
		int			fc_noldmembers;
		int			fc_nnewmembers;
		Oid		   *fc_oldmembers;
		Oid		   *fc_newmembers;
		HeapTuple	fc_tuple;

		fc_tuple = SearchSysCacheLocked1(DATABASEOID, ObjectIdGetDatum(fc_datId));
		if (!HeapTupleIsValid(fc_tuple))
			elog(ERROR, "cache lookup failed for database %u", fc_datId);

		pg_database_tuple = (Form_pg_database) GETSTRUCT(fc_tuple);

		/*
		 * 获取拥有者 ID 和现有 ACL 的工作副本。如果没有 ACL，
		 * 则替换为适当的默认值。
		 */
		fc_ownerId = pg_database_tuple->datdba;
		fc_aclDatum = heap_getattr(fc_tuple, Anum_pg_database_datacl,
								RelationGetDescr(fc_relation), &fc_isNull);
		if (fc_isNull)
		{
			fc_old_acl = acldefault(OBJECT_DATABASE, fc_ownerId);
			/* 根据目录没有旧的成员角色 */
			fc_noldmembers = 0;
			fc_oldmembers = NULL;
		}
		else
		{
			fc_old_acl = DatumGetAclPCopy(fc_aclDatum);
			/* 获取现有 ACL 中提到的角色 */
			fc_noldmembers = aclmembers(fc_old_acl, &fc_oldmembers);
		}

		/* 确定执行授予的 ID，以及可用的授予选项 */
		select_best_grantor(GetUserId(), fc_istmt->privileges,
							fc_old_acl, fc_ownerId,
							&fc_grantorId, &fc_avail_goptions);

		/*
		 * 将权限限制为我们实际可以授予的，并发出
		 * 标准规定的警告和错误消息。
		 */
		fc_this_privileges =
			fc_restrict_and_check_grant(fc_istmt->is_grant, fc_avail_goptions,
									 fc_istmt->all_privs, fc_istmt->privileges,
									 fc_datId, fc_grantorId, OBJECT_DATABASE,
									 NameStr(pg_database_tuple->datname),
									 0, NULL);

		/*
		 * 生成新的 ACL。
		 */
		fc_new_acl = fc_merge_acl_with_grant(fc_old_acl, fc_istmt->is_grant,
									   fc_istmt->grant_option, fc_istmt->behavior,
									   fc_istmt->grantees, fc_this_privileges,
									   fc_grantorId, fc_ownerId);

		/*
		 * 我们需要旧 ACL 和新 ACL 的成员，以便我们可以纠正
		 * 共享依赖信息。
		 */
		fc_nnewmembers = aclmembers(fc_new_acl, &fc_newmembers);

		/* 完成构建新的 ACL 值，现在插入它 */
		MemSet(fc_values, 0, sizeof(fc_values));
		MemSet(fc_nulls, false, sizeof(fc_nulls));
		MemSet(fc_replaces, false, sizeof(fc_replaces));

		fc_replaces[Anum_pg_database_datacl - 1] = true;
		fc_values[Anum_pg_database_datacl - 1] = PointerGetDatum(fc_new_acl);

		fc_newtuple = heap_modify_tuple(fc_tuple, RelationGetDescr(fc_relation), fc_values,
									 fc_nulls, fc_replaces);

		CatalogTupleUpdate(fc_relation, &fc_newtuple->t_self, fc_newtuple);
		UnlockTuple(fc_relation, &fc_tuple->t_self, InplaceUpdateTupleLock);

		/* 更新共享依赖 ACL 信息 */
		updateAclDependencies(DatabaseRelationId, pg_database_tuple->oid, 0,
							  fc_ownerId,
							  fc_noldmembers, fc_oldmembers,
							  fc_nnewmembers, fc_newmembers);

		ReleaseSysCache(fc_tuple);

		pfree(fc_new_acl);

		/* 防止处理重复对象时出错 */
		CommandCounterIncrement();
	}

	table_close(fc_relation, RowExclusiveLock);
}

static void fc_ExecGrant_Fdw(InternalGrant *fc_istmt)
{
	Relation	fc_relation;
	ListCell   *fc_cell;

	if (fc_istmt->all_privs && fc_istmt->privileges == ACL_NO_RIGHTS)
		fc_istmt->privileges = ACL_ALL_RIGHTS_FDW;

	fc_relation = table_open(ForeignDataWrapperRelationId, RowExclusiveLock);

	foreach(fc_cell, fc_istmt->objects)
	{
		Oid			fc_fdwid = lfirst_oid(fc_cell);
		Form_pg_foreign_data_wrapper pg_fdw_tuple;
		Datum		fc_aclDatum;
		bool		fc_isNull;
		AclMode		fc_avail_goptions;
		AclMode		fc_this_privileges;
		Acl		   *fc_old_acl;
		Acl		   *fc_new_acl;
		Oid			fc_grantorId;
		Oid			fc_ownerId;
		HeapTuple	fc_tuple;
		HeapTuple	fc_newtuple;
		Datum		fc_values[Natts_pg_foreign_data_wrapper];
		bool		fc_nulls[Natts_pg_foreign_data_wrapper];
		bool		fc_replaces[Natts_pg_foreign_data_wrapper];
		int			fc_noldmembers;
		int			fc_nnewmembers;
		Oid		   *fc_oldmembers;
		Oid		   *fc_newmembers;

		fc_tuple = SearchSysCache1(FOREIGNDATAWRAPPEROID,
								ObjectIdGetDatum(fc_fdwid));
		if (!HeapTupleIsValid(fc_tuple))
			elog(ERROR, "cache lookup failed for foreign-data wrapper %u", fc_fdwid);

		pg_fdw_tuple = (Form_pg_foreign_data_wrapper) GETSTRUCT(fc_tuple);

		/*
		 * 获取拥有者 ID 和现有 ACL 的工作副本。如果没有 ACL，
		 * 则替换为适当的默认值。
		 */
		fc_ownerId = pg_fdw_tuple->fdwowner;
		fc_aclDatum = SysCacheGetAttr(FOREIGNDATAWRAPPEROID, fc_tuple,
								   Anum_pg_foreign_data_wrapper_fdwacl,
								   &fc_isNull);
		if (fc_isNull)
		{
			fc_old_acl = acldefault(OBJECT_FDW, fc_ownerId);
			/* 根据目录没有旧的成员角色 */
			fc_noldmembers = 0;
			fc_oldmembers = NULL;
		}
		else
		{
			fc_old_acl = DatumGetAclPCopy(fc_aclDatum);
			/* 获取现有 ACL 中提到的角色 */
			fc_noldmembers = aclmembers(fc_old_acl, &fc_oldmembers);
		}

		/* 确定执行授予的 ID，以及可用的授予选项 */
		select_best_grantor(GetUserId(), fc_istmt->privileges,
							fc_old_acl, fc_ownerId,
							&fc_grantorId, &fc_avail_goptions);

		/*
		 * 将权限限制为我们实际可以授予的，并发出
		 * 标准规定的警告和错误消息。
		 */
		fc_this_privileges =
			fc_restrict_and_check_grant(fc_istmt->is_grant, fc_avail_goptions,
									 fc_istmt->all_privs, fc_istmt->privileges,
									 fc_fdwid, fc_grantorId, OBJECT_FDW,
									 NameStr(pg_fdw_tuple->fdwname),
									 0, NULL);

		/*
		 * 生成新的 ACL。
		 */
		fc_new_acl = fc_merge_acl_with_grant(fc_old_acl, fc_istmt->is_grant,
									   fc_istmt->grant_option, fc_istmt->behavior,
									   fc_istmt->grantees, fc_this_privileges,
									   fc_grantorId, fc_ownerId);

		/*
		 * 我们需要旧 ACL 和新 ACL 的成员，以便我们可以纠正
		 * 共享依赖信息。
		 */
		fc_nnewmembers = aclmembers(fc_new_acl, &fc_newmembers);

		/* 完成构建新的 ACL 值，现在插入它 */
		MemSet(fc_values, 0, sizeof(fc_values));
		MemSet(fc_nulls, false, sizeof(fc_nulls));
		MemSet(fc_replaces, false, sizeof(fc_replaces));

		fc_replaces[Anum_pg_foreign_data_wrapper_fdwacl - 1] = true;
		fc_values[Anum_pg_foreign_data_wrapper_fdwacl - 1] = PointerGetDatum(fc_new_acl);

		fc_newtuple = heap_modify_tuple(fc_tuple, RelationGetDescr(fc_relation), fc_values,
									 fc_nulls, fc_replaces);

		CatalogTupleUpdate(fc_relation, &fc_newtuple->t_self, fc_newtuple);

		/* 更新扩展的初始权限 */
		fc_recordExtensionInitPriv(fc_fdwid, ForeignDataWrapperRelationId, 0,
								fc_new_acl);

		/* 更新共享依赖 ACL 信息 */
		updateAclDependencies(ForeignDataWrapperRelationId,
							  pg_fdw_tuple->oid, 0,
							  fc_ownerId,
							  fc_noldmembers, fc_oldmembers,
							  fc_nnewmembers, fc_newmembers);

		ReleaseSysCache(fc_tuple);

		pfree(fc_new_acl);

		/* 防止处理重复对象时出错 */
		CommandCounterIncrement();
	}

	table_close(fc_relation, RowExclusiveLock);
}

static void fc_ExecGrant_ForeignServer(InternalGrant *fc_istmt)
{
	Relation	fc_relation;
	ListCell   *fc_cell;

	if (fc_istmt->all_privs && fc_istmt->privileges == ACL_NO_RIGHTS)
		fc_istmt->privileges = ACL_ALL_RIGHTS_FOREIGN_SERVER;

	fc_relation = table_open(ForeignServerRelationId, RowExclusiveLock);

	foreach(fc_cell, fc_istmt->objects)
	{
		Oid			fc_srvid = lfirst_oid(fc_cell);
		Form_pg_foreign_server pg_server_tuple;
		Datum		fc_aclDatum;
		bool		fc_isNull;
		AclMode		fc_avail_goptions;
		AclMode		fc_this_privileges;
		Acl		   *fc_old_acl;
		Acl		   *fc_new_acl;
		Oid			fc_grantorId;
		Oid			fc_ownerId;
		HeapTuple	fc_tuple;
		HeapTuple	fc_newtuple;
		Datum		fc_values[Natts_pg_foreign_server];
		bool		fc_nulls[Natts_pg_foreign_server];
		bool		fc_replaces[Natts_pg_foreign_server];
		int			fc_noldmembers;
		int			fc_nnewmembers;
		Oid		   *fc_oldmembers;
		Oid		   *fc_newmembers;

		fc_tuple = SearchSysCache1(FOREIGNSERVEROID, ObjectIdGetDatum(fc_srvid));
		if (!HeapTupleIsValid(fc_tuple))
			elog(ERROR, "cache lookup failed for foreign server %u", fc_srvid);

		pg_server_tuple = (Form_pg_foreign_server) GETSTRUCT(fc_tuple);

		/*
		 * 获取拥有者 ID 和现有 ACL 的工作副本。如果没有 ACL，
		 * 则替换为适当的默认值。
		 */
		fc_ownerId = pg_server_tuple->srvowner;
		fc_aclDatum = SysCacheGetAttr(FOREIGNSERVEROID, fc_tuple,
								   Anum_pg_foreign_server_srvacl,
								   &fc_isNull);
		if (fc_isNull)
		{
			fc_old_acl = acldefault(OBJECT_FOREIGN_SERVER, fc_ownerId);
			/* 根据目录没有旧的成员角色 */
			fc_noldmembers = 0;
			fc_oldmembers = NULL;
		}
		else
		{
			fc_old_acl = DatumGetAclPCopy(fc_aclDatum);
			/* 获取现有 ACL 中提到的角色 */
			fc_noldmembers = aclmembers(fc_old_acl, &fc_oldmembers);
		}

		/* 确定执行授予的 ID，以及可用的授予选项 */
		select_best_grantor(GetUserId(), fc_istmt->privileges,
							fc_old_acl, fc_ownerId,
							&fc_grantorId, &fc_avail_goptions);

		/*
		 * 将权限限制为我们实际可以授予的，并发出
		 * 标准规定的警告和错误消息。
		 */
		fc_this_privileges =
			fc_restrict_and_check_grant(fc_istmt->is_grant, fc_avail_goptions,
									 fc_istmt->all_privs, fc_istmt->privileges,
									 fc_srvid, fc_grantorId, OBJECT_FOREIGN_SERVER,
									 NameStr(pg_server_tuple->srvname),
									 0, NULL);

		/*
		 * 生成新的 ACL。
		 */
		fc_new_acl = fc_merge_acl_with_grant(fc_old_acl, fc_istmt->is_grant,
									   fc_istmt->grant_option, fc_istmt->behavior,
									   fc_istmt->grantees, fc_this_privileges,
									   fc_grantorId, fc_ownerId);

		/*
		 * 我们需要旧 ACL 和新 ACL 的成员，以便我们可以纠正
		 * 共享依赖信息。
		 */
		fc_nnewmembers = aclmembers(fc_new_acl, &fc_newmembers);

		/* 完成构建新的 ACL 值，现在插入它 */
		MemSet(fc_values, 0, sizeof(fc_values));
		MemSet(fc_nulls, false, sizeof(fc_nulls));
		MemSet(fc_replaces, false, sizeof(fc_replaces));

		fc_replaces[Anum_pg_foreign_server_srvacl - 1] = true;
		fc_values[Anum_pg_foreign_server_srvacl - 1] = PointerGetDatum(fc_new_acl);

		fc_newtuple = heap_modify_tuple(fc_tuple, RelationGetDescr(fc_relation), fc_values,
									 fc_nulls, fc_replaces);

		CatalogTupleUpdate(fc_relation, &fc_newtuple->t_self, fc_newtuple);

		/* 更新扩展的初始权限 */
		fc_recordExtensionInitPriv(fc_srvid, ForeignServerRelationId, 0, fc_new_acl);

		/* 更新共享依赖 ACL 信息 */
		updateAclDependencies(ForeignServerRelationId,
							  pg_server_tuple->oid, 0,
							  fc_ownerId,
							  fc_noldmembers, fc_oldmembers,
							  fc_nnewmembers, fc_newmembers);

		ReleaseSysCache(fc_tuple);

		pfree(fc_new_acl);

		/* 防止处理重复对象时出错 */
		CommandCounterIncrement();
	}

	table_close(fc_relation, RowExclusiveLock);
}

static void fc_ExecGrant_Function(InternalGrant *fc_istmt)
{
	Relation	fc_relation;
	ListCell   *fc_cell;

	if (fc_istmt->all_privs && fc_istmt->privileges == ACL_NO_RIGHTS)
		fc_istmt->privileges = ACL_ALL_RIGHTS_FUNCTION;

	fc_relation = table_open(ProcedureRelationId, RowExclusiveLock);

	foreach(fc_cell, fc_istmt->objects)
	{
		Oid			fc_funcId = lfirst_oid(fc_cell);
		Form_pg_proc pg_proc_tuple;
		Datum		fc_aclDatum;
		bool		fc_isNull;
		AclMode		fc_avail_goptions;
		AclMode		fc_this_privileges;
		Acl		   *fc_old_acl;
		Acl		   *fc_new_acl;
		Oid			fc_grantorId;
		Oid			fc_ownerId;
		HeapTuple	fc_tuple;
		HeapTuple	fc_newtuple;
		Datum		fc_values[Natts_pg_proc];
		bool		fc_nulls[Natts_pg_proc];
		bool		fc_replaces[Natts_pg_proc];
		int			fc_noldmembers;
		int			fc_nnewmembers;
		Oid		   *fc_oldmembers;
		Oid		   *fc_newmembers;

		fc_tuple = SearchSysCache1(PROCOID, ObjectIdGetDatum(fc_funcId));
		if (!HeapTupleIsValid(fc_tuple))
			elog(ERROR, "cache lookup failed for function %u", fc_funcId);

		pg_proc_tuple = (Form_pg_proc) GETSTRUCT(fc_tuple);

		/*
		 * 获取拥有者 ID 和现有 ACL 的工作副本。如果没有 ACL，
		 * 则替换为适当的默认值。
		 */
		fc_ownerId = pg_proc_tuple->proowner;
		fc_aclDatum = SysCacheGetAttr(PROCOID, fc_tuple, Anum_pg_proc_proacl,
								   &fc_isNull);
		if (fc_isNull)
		{
			fc_old_acl = acldefault(OBJECT_FUNCTION, fc_ownerId);
			/* 根据目录没有旧的成员角色 */
			fc_noldmembers = 0;
			fc_oldmembers = NULL;
		}
		else
		{
			fc_old_acl = DatumGetAclPCopy(fc_aclDatum);
			/* 获取现有 ACL 中提到的角色 */
			fc_noldmembers = aclmembers(fc_old_acl, &fc_oldmembers);
		}

		/* 确定执行授予的 ID，以及可用的授予选项 */
		select_best_grantor(GetUserId(), fc_istmt->privileges,
							fc_old_acl, fc_ownerId,
							&fc_grantorId, &fc_avail_goptions);

		/*
		 * 将权限限制为我们实际可以授予的，并发出
		 * 标准规定的警告和错误消息。
		 */
		fc_this_privileges =
			fc_restrict_and_check_grant(fc_istmt->is_grant, fc_avail_goptions,
									 fc_istmt->all_privs, fc_istmt->privileges,
									 fc_funcId, fc_grantorId, OBJECT_FUNCTION,
									 NameStr(pg_proc_tuple->proname),
									 0, NULL);

		/*
		 * 生成新的 ACL。
		 */
		fc_new_acl = fc_merge_acl_with_grant(fc_old_acl, fc_istmt->is_grant,
									   fc_istmt->grant_option, fc_istmt->behavior,
									   fc_istmt->grantees, fc_this_privileges,
									   fc_grantorId, fc_ownerId);

		/*
		 * 我们需要旧 ACL 和新 ACL 的成员，以便我们可以纠正
		 * 共享依赖信息。
		 */
		fc_nnewmembers = aclmembers(fc_new_acl, &fc_newmembers);

		/* 完成构建新的 ACL 值，现在插入它 */
		MemSet(fc_values, 0, sizeof(fc_values));
		MemSet(fc_nulls, false, sizeof(fc_nulls));
		MemSet(fc_replaces, false, sizeof(fc_replaces));

		fc_replaces[Anum_pg_proc_proacl - 1] = true;
		fc_values[Anum_pg_proc_proacl - 1] = PointerGetDatum(fc_new_acl);

		fc_newtuple = heap_modify_tuple(fc_tuple, RelationGetDescr(fc_relation), fc_values,
									 fc_nulls, fc_replaces);

		CatalogTupleUpdate(fc_relation, &fc_newtuple->t_self, fc_newtuple);

		/* 更新扩展的初始权限 */
		fc_recordExtensionInitPriv(fc_funcId, ProcedureRelationId, 0, fc_new_acl);

		/* 更新共享依赖 ACL 信息 */
		updateAclDependencies(ProcedureRelationId, fc_funcId, 0,
							  fc_ownerId,
							  fc_noldmembers, fc_oldmembers,
							  fc_nnewmembers, fc_newmembers);

		ReleaseSysCache(fc_tuple);

		pfree(fc_new_acl);

		/* 防止处理重复对象时出错 */
		CommandCounterIncrement();
	}

	table_close(fc_relation, RowExclusiveLock);
}

static void fc_ExecGrant_Language(InternalGrant *fc_istmt)
{
	Relation	fc_relation;
	ListCell   *fc_cell;

	if (fc_istmt->all_privs && fc_istmt->privileges == ACL_NO_RIGHTS)
		fc_istmt->privileges = ACL_ALL_RIGHTS_LANGUAGE;

	fc_relation = table_open(LanguageRelationId, RowExclusiveLock);

	foreach(fc_cell, fc_istmt->objects)
	{
		Oid			fc_langId = lfirst_oid(fc_cell);
		Form_pg_language pg_language_tuple;
		Datum		fc_aclDatum;
		bool		fc_isNull;
		AclMode		fc_avail_goptions;
		AclMode		fc_this_privileges;
		Acl		   *fc_old_acl;
		Acl		   *fc_new_acl;
		Oid			fc_grantorId;
		Oid			fc_ownerId;
		HeapTuple	fc_tuple;
		HeapTuple	fc_newtuple;
		Datum		fc_values[Natts_pg_language];
		bool		fc_nulls[Natts_pg_language];
		bool		fc_replaces[Natts_pg_language];
		int			fc_noldmembers;
		int			fc_nnewmembers;
		Oid		   *fc_oldmembers;
		Oid		   *fc_newmembers;

		fc_tuple = SearchSysCache1(LANGOID, ObjectIdGetDatum(fc_langId));
		if (!HeapTupleIsValid(fc_tuple))
			elog(ERROR, "cache lookup failed for language %u", fc_langId);

		pg_language_tuple = (Form_pg_language) GETSTRUCT(fc_tuple);

		if (!pg_language_tuple->lanpltrusted)
			ereport(ERROR,
					(errcode(ERRCODE_WRONG_OBJECT_TYPE),
					 errmsg("language \"%s\" is not trusted",
							NameStr(pg_language_tuple->lanname)),
					 errdetail("GRANT and REVOKE are not allowed on untrusted languages, "
							   "because only superusers can use untrusted languages.")));

		/*
		 * 获取拥有者 ID 和现有 ACL 的工作副本。如果没有 ACL，
		 * 则替换为适当的默认值。
		 */
		fc_ownerId = pg_language_tuple->lanowner;
		fc_aclDatum = SysCacheGetAttr(LANGNAME, fc_tuple, Anum_pg_language_lanacl,
								   &fc_isNull);
		if (fc_isNull)
		{
			fc_old_acl = acldefault(OBJECT_LANGUAGE, fc_ownerId);
			/* 根据目录没有旧的成员角色 */
			fc_noldmembers = 0;
			fc_oldmembers = NULL;
		}
		else
		{
			fc_old_acl = DatumGetAclPCopy(fc_aclDatum);
			/* 获取现有 ACL 中提到的角色 */
			fc_noldmembers = aclmembers(fc_old_acl, &fc_oldmembers);
		}

		/* 确定执行授予的 ID，以及可用的授予选项 */
		select_best_grantor(GetUserId(), fc_istmt->privileges,
							fc_old_acl, fc_ownerId,
							&fc_grantorId, &fc_avail_goptions);

		/*
		 * 将权限限制为我们实际可以授予的，并发出
		 * 标准规定的警告和错误消息。
		 */
		fc_this_privileges =
			fc_restrict_and_check_grant(fc_istmt->is_grant, fc_avail_goptions,
									 fc_istmt->all_privs, fc_istmt->privileges,
									 fc_langId, fc_grantorId, OBJECT_LANGUAGE,
									 NameStr(pg_language_tuple->lanname),
									 0, NULL);

		/*
		 * 生成新的 ACL。
		 */
		fc_new_acl = fc_merge_acl_with_grant(fc_old_acl, fc_istmt->is_grant,
									   fc_istmt->grant_option, fc_istmt->behavior,
									   fc_istmt->grantees, fc_this_privileges,
									   fc_grantorId, fc_ownerId);

		/*
		 * 我们需要旧 ACL 和新 ACL 的成员，以便我们可以纠正
		 * 共享依赖信息。
		 */
		fc_nnewmembers = aclmembers(fc_new_acl, &fc_newmembers);

		/* 完成构建新的 ACL 值，现在插入它 */
		MemSet(fc_values, 0, sizeof(fc_values));
		MemSet(fc_nulls, false, sizeof(fc_nulls));
		MemSet(fc_replaces, false, sizeof(fc_replaces));

		fc_replaces[Anum_pg_language_lanacl - 1] = true;
		fc_values[Anum_pg_language_lanacl - 1] = PointerGetDatum(fc_new_acl);

		fc_newtuple = heap_modify_tuple(fc_tuple, RelationGetDescr(fc_relation), fc_values,
									 fc_nulls, fc_replaces);

		CatalogTupleUpdate(fc_relation, &fc_newtuple->t_self, fc_newtuple);

		/* 更新扩展的初始权限 */
		fc_recordExtensionInitPriv(fc_langId, LanguageRelationId, 0, fc_new_acl);

		/* 更新共享依赖 ACL 信息 */
		updateAclDependencies(LanguageRelationId, pg_language_tuple->oid, 0,
							  fc_ownerId,
							  fc_noldmembers, fc_oldmembers,
							  fc_nnewmembers, fc_newmembers);

		ReleaseSysCache(fc_tuple);

		pfree(fc_new_acl);

		/* 防止处理重复对象时出错 */
		CommandCounterIncrement();
	}

	table_close(fc_relation, RowExclusiveLock);
}

static void fc_ExecGrant_Largeobject(InternalGrant *fc_istmt)
{
	Relation	fc_relation;
	ListCell   *fc_cell;

	if (fc_istmt->all_privs && fc_istmt->privileges == ACL_NO_RIGHTS)
		fc_istmt->privileges = ACL_ALL_RIGHTS_LARGEOBJECT;

	fc_relation = table_open(LargeObjectMetadataRelationId,
						  RowExclusiveLock);

	foreach(fc_cell, fc_istmt->objects)
	{
		Oid			fc_loid = lfirst_oid(fc_cell);
		Form_pg_largeobject_metadata fc_form_lo_meta;
		char		fc_loname[NAMEDATALEN];
		Datum		fc_aclDatum;
		bool		fc_isNull;
		AclMode		fc_avail_goptions;
		AclMode		fc_this_privileges;
		Acl		   *fc_old_acl;
		Acl		   *fc_new_acl;
		Oid			fc_grantorId;
		Oid			fc_ownerId;
		HeapTuple	fc_newtuple;
		Datum		fc_values[Natts_pg_largeobject_metadata];
		bool		fc_nulls[Natts_pg_largeobject_metadata];
		bool		fc_replaces[Natts_pg_largeobject_metadata];
		int			fc_noldmembers;
		int			fc_nnewmembers;
		Oid		   *fc_oldmembers;
		Oid		   *fc_newmembers;
		ScanKeyData fc_entry[1];
		SysScanDesc fc_scan;
		HeapTuple	fc_tuple;

		/* pg_largeobject_metadata 没有 syscache */
		ScanKeyInit(&fc_entry[0],
					Anum_pg_largeobject_metadata_oid,
					BTEqualStrategyNumber, F_OIDEQ,
					ObjectIdGetDatum(fc_loid));

		fc_scan = systable_beginscan(fc_relation,
								  LargeObjectMetadataOidIndexId, true,
								  NULL, 1, fc_entry);

		fc_tuple = systable_getnext(fc_scan);
		if (!HeapTupleIsValid(fc_tuple))
			elog(ERROR, "could not find tuple for large object %u", fc_loid);

		fc_form_lo_meta = (Form_pg_largeobject_metadata) GETSTRUCT(fc_tuple);

		/*
		 * 获取拥有者 ID 和现有 ACL 的工作副本。如果没有 ACL，
		 * 则替换为适当的默认值。
		 */
		fc_ownerId = fc_form_lo_meta->lomowner;
		fc_aclDatum = heap_getattr(fc_tuple,
								Anum_pg_largeobject_metadata_lomacl,
								RelationGetDescr(fc_relation), &fc_isNull);
		if (fc_isNull)
		{
			fc_old_acl = acldefault(OBJECT_LARGEOBJECT, fc_ownerId);
			/* 根据目录没有旧的成员角色 */
			fc_noldmembers = 0;
			fc_oldmembers = NULL;
		}
		else
		{
			fc_old_acl = DatumGetAclPCopy(fc_aclDatum);
			/* 获取现有 ACL 中提到的角色 */
			fc_noldmembers = aclmembers(fc_old_acl, &fc_oldmembers);
		}

		/* 确定执行授予的 ID，以及可用的授予选项 */
		select_best_grantor(GetUserId(), fc_istmt->privileges,
							fc_old_acl, fc_ownerId,
							&fc_grantorId, &fc_avail_goptions);

		/*
		 * 将权限限制为我们实际可以授予的，并发出
		 * 标准规定的警告和错误消息。
		 */
		snprintf(fc_loname, sizeof(fc_loname), "large object %u", fc_loid);
		fc_this_privileges =
			fc_restrict_and_check_grant(fc_istmt->is_grant, fc_avail_goptions,
									 fc_istmt->all_privs, fc_istmt->privileges,
									 fc_loid, fc_grantorId, OBJECT_LARGEOBJECT,
									 fc_loname, 0, NULL);

		/*
		 * 生成新的 ACL。
		 */
		fc_new_acl = fc_merge_acl_with_grant(fc_old_acl, fc_istmt->is_grant,
									   fc_istmt->grant_option, fc_istmt->behavior,
									   fc_istmt->grantees, fc_this_privileges,
									   fc_grantorId, fc_ownerId);

		/*
		 * 我们需要旧 ACL 和新 ACL 的成员，以便我们可以纠正
		 * 共享依赖信息。
		 */
		fc_nnewmembers = aclmembers(fc_new_acl, &fc_newmembers);

		/* 完成构建新的 ACL 值，现在插入它 */
		MemSet(fc_values, 0, sizeof(fc_values));
		MemSet(fc_nulls, false, sizeof(fc_nulls));
		MemSet(fc_replaces, false, sizeof(fc_replaces));

		fc_replaces[Anum_pg_largeobject_metadata_lomacl - 1] = true;
		fc_values[Anum_pg_largeobject_metadata_lomacl - 1]
			= PointerGetDatum(fc_new_acl);

		fc_newtuple = heap_modify_tuple(fc_tuple, RelationGetDescr(fc_relation),
									 fc_values, fc_nulls, fc_replaces);

		CatalogTupleUpdate(fc_relation, &fc_newtuple->t_self, fc_newtuple);

		/* 更新扩展的初始权限 */
		fc_recordExtensionInitPriv(fc_loid, LargeObjectRelationId, 0, fc_new_acl);

		/* 更新共享依赖 ACL 信息 */
		updateAclDependencies(LargeObjectRelationId,
							  fc_form_lo_meta->oid, 0,
							  fc_ownerId,
							  fc_noldmembers, fc_oldmembers,
							  fc_nnewmembers, fc_newmembers);

		systable_endscan(fc_scan);

		pfree(fc_new_acl);

		/* 防止处理重复对象时出错 */
		CommandCounterIncrement();
	}

	table_close(fc_relation, RowExclusiveLock);
}

static void fc_ExecGrant_Namespace(InternalGrant *fc_istmt)
{
	Relation	fc_relation;
	ListCell   *fc_cell;

	if (fc_istmt->all_privs && fc_istmt->privileges == ACL_NO_RIGHTS)
		fc_istmt->privileges = ACL_ALL_RIGHTS_SCHEMA;

	fc_relation = table_open(NamespaceRelationId, RowExclusiveLock);

	foreach(fc_cell, fc_istmt->objects)
	{
		Oid			fc_nspid = lfirst_oid(fc_cell);
		Form_pg_namespace pg_namespace_tuple;
		Datum		fc_aclDatum;
		bool		fc_isNull;
		AclMode		fc_avail_goptions;
		AclMode		fc_this_privileges;
		Acl		   *fc_old_acl;
		Acl		   *fc_new_acl;
		Oid			fc_grantorId;
		Oid			fc_ownerId;
		HeapTuple	fc_tuple;
		HeapTuple	fc_newtuple;
		Datum		fc_values[Natts_pg_namespace];
		bool		fc_nulls[Natts_pg_namespace];
		bool		fc_replaces[Natts_pg_namespace];
		int			fc_noldmembers;
		int			fc_nnewmembers;
		Oid		   *fc_oldmembers;
		Oid		   *fc_newmembers;

		fc_tuple = SearchSysCache1(NAMESPACEOID, ObjectIdGetDatum(fc_nspid));
		if (!HeapTupleIsValid(fc_tuple))
			elog(ERROR, "cache lookup failed for namespace %u", fc_nspid);

		pg_namespace_tuple = (Form_pg_namespace) GETSTRUCT(fc_tuple);

		/*
		 * 获取拥有者 ID 和现有 ACL 的工作副本。如果没有 ACL，
		 * 则替换为适当的默认值。
		 */
		fc_ownerId = pg_namespace_tuple->nspowner;
		fc_aclDatum = SysCacheGetAttr(NAMESPACENAME, fc_tuple,
								   Anum_pg_namespace_nspacl,
								   &fc_isNull);
		if (fc_isNull)
		{
			fc_old_acl = acldefault(OBJECT_SCHEMA, fc_ownerId);
			/* 根据目录没有旧的成员角色 */
			fc_noldmembers = 0;
			fc_oldmembers = NULL;
		}
		else
		{
			fc_old_acl = DatumGetAclPCopy(fc_aclDatum);
			/* 获取现有 ACL 中提到的角色 */
			fc_noldmembers = aclmembers(fc_old_acl, &fc_oldmembers);
		}

		/* 确定执行授予的 ID，以及可用的授予选项 */
		select_best_grantor(GetUserId(), fc_istmt->privileges,
							fc_old_acl, fc_ownerId,
							&fc_grantorId, &fc_avail_goptions);

		/*
		 * 将权限限制为我们实际可以授予的，并发出
		 * 标准规定的警告和错误消息。
		 */
		fc_this_privileges =
			fc_restrict_and_check_grant(fc_istmt->is_grant, fc_avail_goptions,
									 fc_istmt->all_privs, fc_istmt->privileges,
									 fc_nspid, fc_grantorId, OBJECT_SCHEMA,
									 NameStr(pg_namespace_tuple->nspname),
									 0, NULL);

		/*
		 * 生成新的 ACL。
		 */
		fc_new_acl = fc_merge_acl_with_grant(fc_old_acl, fc_istmt->is_grant,
									   fc_istmt->grant_option, fc_istmt->behavior,
									   fc_istmt->grantees, fc_this_privileges,
									   fc_grantorId, fc_ownerId);

		/*
		 * 我们需要旧 ACL 和新 ACL 的成员，以便我们可以纠正
		 * 共享依赖信息。
		 */
		fc_nnewmembers = aclmembers(fc_new_acl, &fc_newmembers);

		/* 完成构建新的 ACL 值，现在插入它 */
		MemSet(fc_values, 0, sizeof(fc_values));
		MemSet(fc_nulls, false, sizeof(fc_nulls));
		MemSet(fc_replaces, false, sizeof(fc_replaces));

		fc_replaces[Anum_pg_namespace_nspacl - 1] = true;
		fc_values[Anum_pg_namespace_nspacl - 1] = PointerGetDatum(fc_new_acl);

		fc_newtuple = heap_modify_tuple(fc_tuple, RelationGetDescr(fc_relation), fc_values,
									 fc_nulls, fc_replaces);

		CatalogTupleUpdate(fc_relation, &fc_newtuple->t_self, fc_newtuple);

		/* 更新扩展的初始权限 */
		fc_recordExtensionInitPriv(fc_nspid, NamespaceRelationId, 0, fc_new_acl);

		/* 更新共享依赖 ACL 信息 */
		updateAclDependencies(NamespaceRelationId, pg_namespace_tuple->oid, 0,
							  fc_ownerId,
							  fc_noldmembers, fc_oldmembers,
							  fc_nnewmembers, fc_newmembers);

		ReleaseSysCache(fc_tuple);

		pfree(fc_new_acl);

		/* 防止处理重复对象时出错 */
		CommandCounterIncrement();
	}

	table_close(fc_relation, RowExclusiveLock);
}

static void fc_ExecGrant_Tablespace(InternalGrant *fc_istmt)
{
	Relation	fc_relation;
	ListCell   *fc_cell;

	if (fc_istmt->all_privs && fc_istmt->privileges == ACL_NO_RIGHTS)
		fc_istmt->privileges = ACL_ALL_RIGHTS_TABLESPACE;

	fc_relation = table_open(TableSpaceRelationId, RowExclusiveLock);

	foreach(fc_cell, fc_istmt->objects)
	{
		Oid			fc_tblId = lfirst_oid(fc_cell);
		Form_pg_tablespace pg_tablespace_tuple;
		Datum		fc_aclDatum;
		bool		fc_isNull;
		AclMode		fc_avail_goptions;
		AclMode		fc_this_privileges;
		Acl		   *fc_old_acl;
		Acl		   *fc_new_acl;
		Oid			fc_grantorId;
		Oid			fc_ownerId;
		HeapTuple	fc_newtuple;
		Datum		fc_values[Natts_pg_tablespace];
		bool		fc_nulls[Natts_pg_tablespace];
		bool		fc_replaces[Natts_pg_tablespace];
		int			fc_noldmembers;
		int			fc_nnewmembers;
		Oid		   *fc_oldmembers;
		Oid		   *fc_newmembers;
		HeapTuple	fc_tuple;

		/* 搜索 pg_tablespace 的 syscache */
		fc_tuple = SearchSysCache1(TABLESPACEOID, ObjectIdGetDatum(fc_tblId));
		if (!HeapTupleIsValid(fc_tuple))
			elog(ERROR, "cache lookup failed for tablespace %u", fc_tblId);

		pg_tablespace_tuple = (Form_pg_tablespace) GETSTRUCT(fc_tuple);

		/*
		 * 获取拥有者 ID 和现有 ACL 的工作副本。如果没有 ACL，
		 * 则替换为适当的默认值。
		 */
		fc_ownerId = pg_tablespace_tuple->spcowner;
		fc_aclDatum = heap_getattr(fc_tuple, Anum_pg_tablespace_spcacl,
								RelationGetDescr(fc_relation), &fc_isNull);
		if (fc_isNull)
		{
			fc_old_acl = acldefault(OBJECT_TABLESPACE, fc_ownerId);
			/* 根据目录没有旧的成员角色 */
			fc_noldmembers = 0;
			fc_oldmembers = NULL;
		}
		else
		{
			fc_old_acl = DatumGetAclPCopy(fc_aclDatum);
			/* 获取现有 ACL 中提到的角色 */
			fc_noldmembers = aclmembers(fc_old_acl, &fc_oldmembers);
		}

		/* 确定执行授予的 ID，以及可用的授予选项 */
		select_best_grantor(GetUserId(), fc_istmt->privileges,
							fc_old_acl, fc_ownerId,
							&fc_grantorId, &fc_avail_goptions);

		/*
		 * 将权限限制为我们实际可以授予的，并发出
		 * 标准规定的警告和错误消息。
		 */
		fc_this_privileges =
			fc_restrict_and_check_grant(fc_istmt->is_grant, fc_avail_goptions,
									 fc_istmt->all_privs, fc_istmt->privileges,
									 fc_tblId, fc_grantorId, OBJECT_TABLESPACE,
									 NameStr(pg_tablespace_tuple->spcname),
									 0, NULL);

		/*
		 * 生成新的 ACL。
		 */
		fc_new_acl = fc_merge_acl_with_grant(fc_old_acl, fc_istmt->is_grant,
									   fc_istmt->grant_option, fc_istmt->behavior,
									   fc_istmt->grantees, fc_this_privileges,
									   fc_grantorId, fc_ownerId);

		/*
		 * 我们需要旧 ACL 和新 ACL 的成员，以便我们可以纠正
		 * 共享依赖信息。
		 */
		fc_nnewmembers = aclmembers(fc_new_acl, &fc_newmembers);

		/* 完成构建新的 ACL 值，现在插入它 */
		MemSet(fc_values, 0, sizeof(fc_values));
		MemSet(fc_nulls, false, sizeof(fc_nulls));
		MemSet(fc_replaces, false, sizeof(fc_replaces));

		fc_replaces[Anum_pg_tablespace_spcacl - 1] = true;
		fc_values[Anum_pg_tablespace_spcacl - 1] = PointerGetDatum(fc_new_acl);

		fc_newtuple = heap_modify_tuple(fc_tuple, RelationGetDescr(fc_relation), fc_values,
									 fc_nulls, fc_replaces);

		CatalogTupleUpdate(fc_relation, &fc_newtuple->t_self, fc_newtuple);

		/* 更新共享依赖 ACL 信息 */
		updateAclDependencies(TableSpaceRelationId, fc_tblId, 0,
							  fc_ownerId,
							  fc_noldmembers, fc_oldmembers,
							  fc_nnewmembers, fc_newmembers);

		ReleaseSysCache(fc_tuple);
		pfree(fc_new_acl);

		/* 防止处理重复对象时出错 */
		CommandCounterIncrement();
	}

	table_close(fc_relation, RowExclusiveLock);
}

static void fc_ExecGrant_Type(InternalGrant *fc_istmt)
{
	Relation	fc_relation;
	ListCell   *fc_cell;

	if (fc_istmt->all_privs && fc_istmt->privileges == ACL_NO_RIGHTS)
		fc_istmt->privileges = ACL_ALL_RIGHTS_TYPE;

	fc_relation = table_open(TypeRelationId, RowExclusiveLock);

	foreach(fc_cell, fc_istmt->objects)
	{
		Oid			fc_typId = lfirst_oid(fc_cell);
		Form_pg_type pg_type_tuple;
		Datum		fc_aclDatum;
		bool		fc_isNull;
		AclMode		fc_avail_goptions;
		AclMode		fc_this_privileges;
		Acl		   *fc_old_acl;
		Acl		   *fc_new_acl;
		Oid			fc_grantorId;
		Oid			fc_ownerId;
		HeapTuple	fc_newtuple;
		Datum		fc_values[Natts_pg_type];
		bool		fc_nulls[Natts_pg_type];
		bool		fc_replaces[Natts_pg_type];
		int			fc_noldmembers;
		int			fc_nnewmembers;
		Oid		   *fc_oldmembers;
		Oid		   *fc_newmembers;
		HeapTuple	fc_tuple;

		
/* 搜索 syscache 中的 pg_type */
		fc_tuple = SearchSysCache1(TYPEOID, ObjectIdGetDatum(fc_typId));
		if (!HeapTupleIsValid(fc_tuple))
			elog(ERROR, "cache lookup failed for type %u", fc_typId);

		pg_type_tuple = (Form_pg_type) GETSTRUCT(fc_tuple);

		if (IsTrueArrayType(pg_type_tuple))
			ereport(ERROR,
					(errcode(ERRCODE_INVALID_GRANT_OPERATION),
					 errmsg("cannot set privileges of array types"),
					 errhint("Set the privileges of the element type instead.")));

		/* 对一个非域使用 GRANT DOMAIN？ */
		if (fc_istmt->objtype == OBJECT_DOMAIN &&
			pg_type_tuple->typtype != TYPTYPE_DOMAIN)
			ereport(ERROR,
					(errcode(ERRCODE_WRONG_OBJECT_TYPE),
					 errmsg("\"%s\" is not a domain",
							NameStr(pg_type_tuple->typname))));

		/*
		 * 获取拥有者 ID 和现有 ACL 的工作副本。如果没有 ACL，
		 * 则替换为适当的默认值。
		 */
		fc_ownerId = pg_type_tuple->typowner;
		fc_aclDatum = heap_getattr(fc_tuple, Anum_pg_type_typacl,
								RelationGetDescr(fc_relation), &fc_isNull);
		if (fc_isNull)
		{
			fc_old_acl = acldefault(fc_istmt->objtype, fc_ownerId);
			/* 根据目录没有旧的成员角色 */
			fc_noldmembers = 0;
			fc_oldmembers = NULL;
		}
		else
		{
			fc_old_acl = DatumGetAclPCopy(fc_aclDatum);
			/* 获取现有 ACL 中提到的角色 */
			fc_noldmembers = aclmembers(fc_old_acl, &fc_oldmembers);
		}

		/* 确定执行授予的 ID，以及可用的授予选项 */
		select_best_grantor(GetUserId(), fc_istmt->privileges,
							fc_old_acl, fc_ownerId,
							&fc_grantorId, &fc_avail_goptions);

		/*
		 * 将权限限制为我们实际可以授予的，并发出
		 * 标准规定的警告和错误消息。
		 */
		fc_this_privileges =
			fc_restrict_and_check_grant(fc_istmt->is_grant, fc_avail_goptions,
									 fc_istmt->all_privs, fc_istmt->privileges,
									 fc_typId, fc_grantorId, OBJECT_TYPE,
									 NameStr(pg_type_tuple->typname),
									 0, NULL);

		/*
		 * 生成新的 ACL。
		 */
		fc_new_acl = fc_merge_acl_with_grant(fc_old_acl, fc_istmt->is_grant,
									   fc_istmt->grant_option, fc_istmt->behavior,
									   fc_istmt->grantees, fc_this_privileges,
									   fc_grantorId, fc_ownerId);

		/*
		 * 我们需要旧 ACL 和新 ACL 的成员，以便我们可以纠正
		 * 共享依赖信息。
		 */
		fc_nnewmembers = aclmembers(fc_new_acl, &fc_newmembers);

		/* 完成构建新的 ACL 值，现在插入它 */
		MemSet(fc_values, 0, sizeof(fc_values));
		MemSet(fc_nulls, false, sizeof(fc_nulls));
		MemSet(fc_replaces, false, sizeof(fc_replaces));

		fc_replaces[Anum_pg_type_typacl - 1] = true;
		fc_values[Anum_pg_type_typacl - 1] = PointerGetDatum(fc_new_acl);

		fc_newtuple = heap_modify_tuple(fc_tuple, RelationGetDescr(fc_relation), fc_values,
									 fc_nulls, fc_replaces);

		CatalogTupleUpdate(fc_relation, &fc_newtuple->t_self, fc_newtuple);

		/* 更新扩展的初始权限 */
		fc_recordExtensionInitPriv(fc_typId, TypeRelationId, 0, fc_new_acl);

		/* 更新共享依赖 ACL 信息 */
		updateAclDependencies(TypeRelationId, fc_typId, 0,
							  fc_ownerId,
							  fc_noldmembers, fc_oldmembers,
							  fc_nnewmembers, fc_newmembers);

		ReleaseSysCache(fc_tuple);
		pfree(fc_new_acl);

		/* 防止处理重复对象时出错 */
		CommandCounterIncrement();
	}

	table_close(fc_relation, RowExclusiveLock);
}

static void fc_ExecGrant_Parameter(InternalGrant *fc_istmt)
{
	Relation	fc_relation;
	ListCell   *fc_cell;

	if (fc_istmt->all_privs && fc_istmt->privileges == ACL_NO_RIGHTS)
		fc_istmt->privileges = ACL_ALL_RIGHTS_PARAMETER_ACL;

	fc_relation = table_open(ParameterAclRelationId, RowExclusiveLock);

	foreach(fc_cell, fc_istmt->objects)
	{
		Oid			fc_parameterId = lfirst_oid(fc_cell);
		Datum		fc_nameDatum;
		const char *fc_parname;
		Datum		fc_aclDatum;
		bool		fc_isNull;
		AclMode		fc_avail_goptions;
		AclMode		fc_this_privileges;
		Acl		   *fc_old_acl;
		Acl		   *fc_new_acl;
		Oid			fc_grantorId;
		Oid			fc_ownerId;
		HeapTuple	fc_tuple;
		int			fc_noldmembers;
		int			fc_nnewmembers;
		Oid		   *fc_oldmembers;
		Oid		   *fc_newmembers;

		fc_tuple = SearchSysCache1(PARAMETERACLOID, ObjectIdGetDatum(fc_parameterId));
		if (!HeapTupleIsValid(fc_tuple))
			elog(ERROR, "cache lookup failed for parameter ACL %u",
				 fc_parameterId);

		/* 我们需要 GUC 的名称 */
		fc_nameDatum = SysCacheGetAttr(PARAMETERACLOID, fc_tuple,
									Anum_pg_parameter_acl_parname,
									&fc_isNull);
		Assert(!fc_isNull);
		fc_parname = TextDatumGetCString(fc_nameDatum);

		/* 将所有参数视为属于引导超级用户。 */
		fc_ownerId = BOOTSTRAP_SUPERUSERID;

		/*
		 * 获取现有 ACL 的工作副本。如果没有 ACL，则替换为
		 * 正确的默认值。
		 */
		fc_aclDatum = SysCacheGetAttr(PARAMETERACLOID, fc_tuple,
								   Anum_pg_parameter_acl_paracl,
								   &fc_isNull);

		if (fc_isNull)
		{
			fc_old_acl = acldefault(fc_istmt->objtype, fc_ownerId);
			/* 根据目录没有旧的成员角色 */
			fc_noldmembers = 0;
			fc_oldmembers = NULL;
		}
		else
		{
			fc_old_acl = DatumGetAclPCopy(fc_aclDatum);
			/* 获取现有 ACL 中提到的角色 */
			fc_noldmembers = aclmembers(fc_old_acl, &fc_oldmembers);
		}

		/* 确定执行授予的 ID，以及可用的授予选项 */
		select_best_grantor(GetUserId(), fc_istmt->privileges,
							fc_old_acl, fc_ownerId,
							&fc_grantorId, &fc_avail_goptions);

		/*
		 * 将权限限制为我们实际可以授予的，并发出
		 * 标准规定的警告和错误消息。
		 */
		fc_this_privileges =
			fc_restrict_and_check_grant(fc_istmt->is_grant, fc_avail_goptions,
									 fc_istmt->all_privs, fc_istmt->privileges,
									 fc_parameterId, fc_grantorId,
									 OBJECT_PARAMETER_ACL,
									 fc_parname,
									 0, NULL);

		/*
		 * 生成新的 ACL。
		 */
		fc_new_acl = fc_merge_acl_with_grant(fc_old_acl, fc_istmt->is_grant,
									   fc_istmt->grant_option, fc_istmt->behavior,
									   fc_istmt->grantees, fc_this_privileges,
									   fc_grantorId, fc_ownerId);

		/*
		 * 我们需要旧 ACL 和新 ACL 的成员，以便我们可以纠正
		 * 共享依赖信息。
		 */
		fc_nnewmembers = aclmembers(fc_new_acl, &fc_newmembers);

		/*
		 * 如果新的 ACL 等于默认值，我们不再需要目录
		 * 条目。删除它而不是更新它，以避免
		 * 留下一个退化条目。
		 */
		if (aclequal(fc_new_acl, acldefault(fc_istmt->objtype, fc_ownerId)))
		{
			CatalogTupleDelete(fc_relation, &fc_tuple->t_self);
		}
		else
		{
			/* 完成构建新的 ACL 值，现在插入它 */
			HeapTuple	fc_newtuple;
			Datum		fc_values[Natts_pg_parameter_acl];
			bool		fc_nulls[Natts_pg_parameter_acl];
			bool		fc_replaces[Natts_pg_parameter_acl];

			MemSet(fc_values, 0, sizeof(fc_values));
			MemSet(fc_nulls, false, sizeof(fc_nulls));
			MemSet(fc_replaces, false, sizeof(fc_replaces));

			fc_replaces[Anum_pg_parameter_acl_paracl - 1] = true;
			fc_values[Anum_pg_parameter_acl_paracl - 1] = PointerGetDatum(fc_new_acl);

			fc_newtuple = heap_modify_tuple(fc_tuple, RelationGetDescr(fc_relation),
										 fc_values, fc_nulls, fc_replaces);

			CatalogTupleUpdate(fc_relation, &fc_newtuple->t_self, fc_newtuple);
		}

		/* 更新扩展的初始权限 */
		fc_recordExtensionInitPriv(fc_parameterId, ParameterAclRelationId, 0,
								fc_new_acl);

		/* 更新共享依赖 ACL 信息 */
		updateAclDependencies(ParameterAclRelationId, fc_parameterId, 0,
							  fc_ownerId,
							  fc_noldmembers, fc_oldmembers,
							  fc_nnewmembers, fc_newmembers);

		ReleaseSysCache(fc_tuple);
		pfree(fc_new_acl);

		/* 防止处理重复对象时出错 */
		CommandCounterIncrement();
	}

	table_close(fc_relation, RowExclusiveLock);
}


static AclMode fc_string_to_privilege(const char *fc_privname)
{
	if (strcmp(fc_privname, "insert") == 0)
		return ACL_INSERT;
	if (strcmp(fc_privname, "select") == 0)
		return ACL_SELECT;
	if (strcmp(fc_privname, "update") == 0)
		return ACL_UPDATE;
	if (strcmp(fc_privname, "delete") == 0)
		return ACL_DELETE;
	if (strcmp(fc_privname, "truncate") == 0)
		return ACL_TRUNCATE;
	if (strcmp(fc_privname, "references") == 0)
		return ACL_REFERENCES;
	if (strcmp(fc_privname, "trigger") == 0)
		return ACL_TRIGGER;
	if (strcmp(fc_privname, "execute") == 0)
		return ACL_EXECUTE;
	if (strcmp(fc_privname, "usage") == 0)
		return ACL_USAGE;
	if (strcmp(fc_privname, "create") == 0)
		return ACL_CREATE;
	if (strcmp(fc_privname, "temporary") == 0)
		return ACL_CREATE_TEMP;
	if (strcmp(fc_privname, "temp") == 0)
		return ACL_CREATE_TEMP;
	if (strcmp(fc_privname, "connect") == 0)
		return ACL_CONNECT;
	if (strcmp(fc_privname, "set") == 0)
		return ACL_SET;
	if (strcmp(fc_privname, "alter system") == 0)
		return ACL_ALTER_SYSTEM;
	if (strcmp(fc_privname, "rule") == 0)
		return 0;				/* 忽略旧的 RULE 特权 */
	ereport(ERROR,
			(errcode(ERRCODE_SYNTAX_ERROR),
			 errmsg("unrecognized privilege type \"%s\"", fc_privname)));
	return 0;					/* 以安抚编译器 */
}

static const char * fc_privilege_to_string(AclMode fc_privilege)
{
	switch (fc_privilege)
	{
		case ACL_INSERT:
			return "INSERT";
		case ACL_SELECT:
			return "SELECT";
		case ACL_UPDATE:
			return "UPDATE";
		case ACL_DELETE:
			return "DELETE";
		case ACL_TRUNCATE:
			return "TRUNCATE";
		case ACL_REFERENCES:
			return "REFERENCES";
		case ACL_TRIGGER:
			return "TRIGGER";
		case ACL_EXECUTE:
			return "EXECUTE";
		case ACL_USAGE:
			return "USAGE";
		case ACL_CREATE:
			return "CREATE";
		case ACL_CREATE_TEMP:
			return "TEMP";
		case ACL_CONNECT:
			return "CONNECT";
		case ACL_SET:
			return "SET";
		case ACL_ALTER_SYSTEM:
			return "ALTER SYSTEM";
		default:
			elog(ERROR, "unrecognized privilege: %d", (int) fc_privilege);
	}
	return NULL;				/* 以安抚编译器 */
}

/*
 * 标准化报告 aclcheck 权限失败。
 *
 * 注意：我们不对下面的 %s 进行双引号，因为许多调用者
 * 提供的字符串可能已经被引号包围。
 */
void aclcheck_error(AclResult fc_aclerr, ObjectType fc_objtype,
			   const char *fc_objectname)
{
	switch (fc_aclerr)
	{
		case ACLCHECK_OK:
			/* 没有错误，因此返回给调用者 */
			break;
		case ACLCHECK_NO_PRIV:
			{
				const char *fc_msg = "???";

				switch (fc_objtype)
				{
					case OBJECT_AGGREGATE:
						fc_msg = gettext_noop("permission denied for aggregate %s");
						break;
					case OBJECT_COLLATION:
						fc_msg = gettext_noop("permission denied for collation %s");
						break;
					case OBJECT_COLUMN:
						fc_msg = gettext_noop("permission denied for column %s");
						break;
					case OBJECT_CONVERSION:
						fc_msg = gettext_noop("permission denied for conversion %s");
						break;
					case OBJECT_DATABASE:
						fc_msg = gettext_noop("permission denied for database %s");
						break;
					case OBJECT_DOMAIN:
						fc_msg = gettext_noop("permission denied for domain %s");
						break;
					case OBJECT_EVENT_TRIGGER:
						fc_msg = gettext_noop("permission denied for event trigger %s");
						break;
					case OBJECT_EXTENSION:
						fc_msg = gettext_noop("permission denied for extension %s");
						break;
					case OBJECT_FDW:
						fc_msg = gettext_noop("permission denied for foreign-data wrapper %s");
						break;
					case OBJECT_FOREIGN_SERVER:
						fc_msg = gettext_noop("permission denied for foreign server %s");
						break;
					case OBJECT_FOREIGN_TABLE:
						fc_msg = gettext_noop("permission denied for foreign table %s");
						break;
					case OBJECT_FUNCTION:
						fc_msg = gettext_noop("permission denied for function %s");
						break;
					case OBJECT_INDEX:
						fc_msg = gettext_noop("permission denied for index %s");
						break;
					case OBJECT_LANGUAGE:
						fc_msg = gettext_noop("permission denied for language %s");
						break;
					case OBJECT_LARGEOBJECT:
						fc_msg = gettext_noop("permission denied for large object %s");
						break;
					case OBJECT_MATVIEW:
						fc_msg = gettext_noop("permission denied for materialized view %s");
						break;
					case OBJECT_OPCLASS:
						fc_msg = gettext_noop("permission denied for operator class %s");
						break;
					case OBJECT_OPERATOR:
						fc_msg = gettext_noop("permission denied for operator %s");
						break;
					case OBJECT_OPFAMILY:
						fc_msg = gettext_noop("permission denied for operator family %s");
						break;
					case OBJECT_PARAMETER_ACL:
						fc_msg = gettext_noop("permission denied for parameter %s");
						break;
					case OBJECT_POLICY:
						fc_msg = gettext_noop("permission denied for policy %s");
						break;
					case OBJECT_PROCEDURE:
						fc_msg = gettext_noop("permission denied for procedure %s");
						break;
					case OBJECT_PUBLICATION:
						fc_msg = gettext_noop("permission denied for publication %s");
						break;
					case OBJECT_ROUTINE:
						fc_msg = gettext_noop("permission denied for routine %s");
						break;
					case OBJECT_SCHEMA:
						fc_msg = gettext_noop("permission denied for schema %s");
						break;
					case OBJECT_SEQUENCE:
						fc_msg = gettext_noop("permission denied for sequence %s");
						break;
					case OBJECT_STATISTIC_EXT:
						fc_msg = gettext_noop("permission denied for statistics object %s");
						break;
					case OBJECT_SUBSCRIPTION:
						fc_msg = gettext_noop("permission denied for subscription %s");
						break;
					case OBJECT_TABLE:
						fc_msg = gettext_noop("permission denied for table %s");
						break;
					case OBJECT_TABLESPACE:
						fc_msg = gettext_noop("permission denied for tablespace %s");
						break;
					case OBJECT_TSCONFIGURATION:
						fc_msg = gettext_noop("permission denied for text search configuration %s");
						break;
					case OBJECT_TSDICTIONARY:
						fc_msg = gettext_noop("permission denied for text search dictionary %s");
						break;
					case OBJECT_TYPE:
						fc_msg = gettext_noop("permission denied for type %s");
						break;
					case OBJECT_VIEW:
						fc_msg = gettext_noop("permission denied for view %s");
						break;
						/* 这些目前未使用 */
					case OBJECT_ACCESS_METHOD:
					case OBJECT_AMOP:
					case OBJECT_AMPROC:
					case OBJECT_ATTRIBUTE:
					case OBJECT_CAST:
					case OBJECT_DEFAULT:
					case OBJECT_DEFACL:
					case OBJECT_DOMCONSTRAINT:
					case OBJECT_PUBLICATION_NAMESPACE:
					case OBJECT_PUBLICATION_REL:
					case OBJECT_ROLE:
					case OBJECT_RULE:
					case OBJECT_TABCONSTRAINT:
					case OBJECT_TRANSFORM:
					case OBJECT_TRIGGER:
					case OBJECT_TSPARSER:
					case OBJECT_TSTEMPLATE:
					case OBJECT_USER_MAPPING:
						elog(ERROR, "unsupported object type %d", fc_objtype);
				}

				ereport(ERROR,
						(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
						 errmsg(fc_msg, fc_objectname)));
				break;
			}
		case ACLCHECK_NOT_OWNER:
			{
				const char *fc_msg = "???";

				switch (fc_objtype)
				{
					case OBJECT_AGGREGATE:
						fc_msg = gettext_noop("must be owner of aggregate %s");
						break;
					case OBJECT_COLLATION:
						fc_msg = gettext_noop("must be owner of collation %s");
						break;
					case OBJECT_CONVERSION:
						fc_msg = gettext_noop("must be owner of conversion %s");
						break;
					case OBJECT_DATABASE:
						fc_msg = gettext_noop("must be owner of database %s");
						break;
					case OBJECT_DOMAIN:
						fc_msg = gettext_noop("must be owner of domain %s");
						break;
					case OBJECT_EVENT_TRIGGER:
						fc_msg = gettext_noop("must be owner of event trigger %s");
						break;
					case OBJECT_EXTENSION:
						fc_msg = gettext_noop("must be owner of extension %s");
						break;
					case OBJECT_FDW:
						fc_msg = gettext_noop("must be owner of foreign-data wrapper %s");
						break;
					case OBJECT_FOREIGN_SERVER:
						fc_msg = gettext_noop("must be owner of foreign server %s");
						break;
					case OBJECT_FOREIGN_TABLE:
						fc_msg = gettext_noop("must be owner of foreign table %s");
						break;
					case OBJECT_FUNCTION:
						fc_msg = gettext_noop("must be owner of function %s");
						break;
					case OBJECT_INDEX:
						fc_msg = gettext_noop("must be owner of index %s");
						break;
					case OBJECT_LANGUAGE:
						fc_msg = gettext_noop("must be owner of language %s");
						break;
					case OBJECT_LARGEOBJECT:
						fc_msg = gettext_noop("must be owner of large object %s");
						break;
					case OBJECT_MATVIEW:
						fc_msg = gettext_noop("must be owner of materialized view %s");
						break;
					case OBJECT_OPCLASS:
						fc_msg = gettext_noop("must be owner of operator class %s");
						break;
					case OBJECT_OPERATOR:
						fc_msg = gettext_noop("must be owner of operator %s");
						break;
					case OBJECT_OPFAMILY:
						fc_msg = gettext_noop("must be owner of operator family %s");
						break;
					case OBJECT_PROCEDURE:
						fc_msg = gettext_noop("must be owner of procedure %s");
						break;
					case OBJECT_PUBLICATION:
						fc_msg = gettext_noop("must be owner of publication %s");
						break;
					case OBJECT_ROUTINE:
						fc_msg = gettext_noop("must be owner of routine %s");
						break;
					case OBJECT_SEQUENCE:
						fc_msg = gettext_noop("must be owner of sequence %s");
						break;
					case OBJECT_SUBSCRIPTION:
						fc_msg = gettext_noop("must be owner of subscription %s");
						break;
					case OBJECT_TABLE:
						fc_msg = gettext_noop("must be owner of table %s");
						break;
					case OBJECT_TYPE:
						fc_msg = gettext_noop("must be owner of type %s");
						break;
					case OBJECT_VIEW:
						fc_msg = gettext_noop("must be owner of view %s");
						break;
					case OBJECT_SCHEMA:
						fc_msg = gettext_noop("must be owner of schema %s");
						break;
					case OBJECT_STATISTIC_EXT:
						fc_msg = gettext_noop("must be owner of statistics object %s");
						break;
					case OBJECT_TABLESPACE:
						fc_msg = gettext_noop("must be owner of tablespace %s");
						break;
					case OBJECT_TSCONFIGURATION:
						fc_msg = gettext_noop("must be owner of text search configuration %s");
						break;
					case OBJECT_TSDICTIONARY:
						fc_msg = gettext_noop("must be owner of text search dictionary %s");
						break;

						/*
						 * 特殊情况：对于这些，错误消息提到
						 * “关系”，因为所有权附加在这里。另见
						 * check_object_ownership()。
						 */
					case OBJECT_COLUMN:
					case OBJECT_POLICY:
					case OBJECT_RULE:
					case OBJECT_TABCONSTRAINT:
					case OBJECT_TRIGGER:
						fc_msg = gettext_noop("must be owner of relation %s");
						break;
						/* 这些目前未使用 */
					case OBJECT_ACCESS_METHOD:
					case OBJECT_AMOP:
					case OBJECT_AMPROC:
					case OBJECT_ATTRIBUTE:
					case OBJECT_CAST:
					case OBJECT_DEFAULT:
					case OBJECT_DEFACL:
					case OBJECT_DOMCONSTRAINT:
					case OBJECT_PARAMETER_ACL:
					case OBJECT_PUBLICATION_NAMESPACE:
					case OBJECT_PUBLICATION_REL:
					case OBJECT_ROLE:
					case OBJECT_TRANSFORM:
					case OBJECT_TSPARSER:
					case OBJECT_TSTEMPLATE:
					case OBJECT_USER_MAPPING:
						elog(ERROR, "unsupported object type %d", fc_objtype);
				}

				ereport(ERROR,
						(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
						 errmsg(fc_msg, fc_objectname)));
				break;
			}
		default:
			elog(ERROR, "unrecognized AclResult: %d", (int) fc_aclerr);
			break;
	}
}


void aclcheck_error_col(AclResult fc_aclerr, ObjectType fc_objtype,
				   const char *fc_objectname, const char *fc_colname)
{
	switch (fc_aclerr)
	{
		case ACLCHECK_OK:
			/* 没有错误，因此返回给调用者 */
			break;
		case ACLCHECK_NO_PRIV:
			ereport(ERROR,
					(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
					 errmsg("permission denied for column \"%s\" of relation \"%s\"",
							fc_colname, fc_objectname)));
			break;
		case ACLCHECK_NOT_OWNER:
			/* 关系消息是可以的，因为列没有单独的所有者 */
			aclcheck_error(fc_aclerr, fc_objtype, fc_objectname);
			break;
		default:
			elog(ERROR, "unrecognized AclResult: %d", (int) fc_aclerr);
			break;
	}
}


/*
 * 特殊的类型处理：使用元素类型而不是数组类型，
 * 并格式化为合适的格式
 */
void aclcheck_error_type(AclResult fc_aclerr, Oid fc_typeOid)
{
	Oid			fc_element_type = get_element_type(fc_typeOid);

	aclcheck_error(fc_aclerr, OBJECT_TYPE, format_type_be(fc_element_type ? fc_element_type : fc_typeOid));
}


/*
 * 根据对象种类中转各种 pg_*_mask 例程
 */
static AclMode pg_aclmask(ObjectType fc_objtype, Oid fc_table_oid, AttrNumber fc_attnum, Oid fc_roleid,
		   AclMode fc_mask, AclMaskHow fc_how)
{
	switch (fc_objtype)
	{
		case OBJECT_COLUMN:
			return
				pg_class_aclmask(fc_table_oid, fc_roleid, fc_mask, fc_how) |
				pg_attribute_aclmask(fc_table_oid, fc_attnum, fc_roleid, fc_mask, fc_how);
		case OBJECT_TABLE:
		case OBJECT_SEQUENCE:
			return pg_class_aclmask(fc_table_oid, fc_roleid, fc_mask, fc_how);
		case OBJECT_DATABASE:
			return pg_database_aclmask(fc_table_oid, fc_roleid, fc_mask, fc_how);
		case OBJECT_FUNCTION:
			return pg_proc_aclmask(fc_table_oid, fc_roleid, fc_mask, fc_how);
		case OBJECT_LANGUAGE:
			return pg_language_aclmask(fc_table_oid, fc_roleid, fc_mask, fc_how);
		case OBJECT_LARGEOBJECT:
			return pg_largeobject_aclmask_snapshot(fc_table_oid, fc_roleid,
												   fc_mask, fc_how, NULL);
		case OBJECT_PARAMETER_ACL:
			return pg_parameter_acl_aclmask(fc_table_oid, fc_roleid, fc_mask, fc_how);
		case OBJECT_SCHEMA:
			return pg_namespace_aclmask(fc_table_oid, fc_roleid, fc_mask, fc_how);
		case OBJECT_STATISTIC_EXT:
			elog(ERROR, "grantable rights not supported for statistics objects");
			/* 未达到，但保持编译器安静 */
			return ACL_NO_RIGHTS;
		case OBJECT_TABLESPACE:
			return pg_tablespace_aclmask(fc_table_oid, fc_roleid, fc_mask, fc_how);
		case OBJECT_FDW:
			return pg_foreign_data_wrapper_aclmask(fc_table_oid, fc_roleid, fc_mask, fc_how);
		case OBJECT_FOREIGN_SERVER:
			return pg_foreign_server_aclmask(fc_table_oid, fc_roleid, fc_mask, fc_how);
		case OBJECT_EVENT_TRIGGER:
			elog(ERROR, "grantable rights not supported for event triggers");
			/* 未达到，但保持编译器安静 */
			return ACL_NO_RIGHTS;
		case OBJECT_TYPE:
			return pg_type_aclmask(fc_table_oid, fc_roleid, fc_mask, fc_how);
		default:
			elog(ERROR, "unrecognized objtype: %d",
				 (int) fc_objtype);
			/* 未达到，但保持编译器安静 */
			return ACL_NO_RIGHTS;
	}
}


/* ****************************************************************
 * 导出的例程，用于检查用户对各种对象的权限
 *
 * 请参见 aclmask() 了解这些函数的公共 API 描述。
 *
 * 注意：我们对查找失败进行了完整的 ereport 处理，因为
 * has_xxx_privilege() 系列函数允许用户将任何随机
 * OID 传递给这些函数。
 * ****************************************************************
 */

/*
 * 导出的例程，用于检查用户对列的权限
 *
 * 注意：这仅考虑专门授予列的权限。
 * 调用者有责任根据需要考虑关系级别的权限。
 * （出于同样的原因，我们在这里没有超级用户的特殊情况。）
 */
AclMode pg_attribute_aclmask(Oid fc_table_oid, AttrNumber fc_attnum, Oid fc_roleid,
					 AclMode fc_mask, AclMaskHow fc_how)
{
	return pg_attribute_aclmask_ext(fc_table_oid, fc_attnum, fc_roleid,
									fc_mask, fc_how, NULL);
}

/*
 * 导出的例程，用于检查用户对列的权限
 *
 * 为 pg_attribute_aclmask() 执行大部分工作，并允许其他
 * 调用者在 is_missing 非 NULL 时避免缺失属性的错误。
 */
AclMode pg_attribute_aclmask_ext(Oid fc_table_oid, AttrNumber fc_attnum, Oid fc_roleid,
						 AclMode fc_mask, AclMaskHow fc_how, bool *fc_is_missing)
{
	AclMode		fc_result;
	HeapTuple	fc_classTuple;
	HeapTuple	fc_attTuple;
	Form_pg_class fc_classForm;
	Form_pg_attribute fc_attributeForm;
	Datum		fc_aclDatum;
	bool		fc_isNull;
	Acl		   *fc_acl;
	Oid			fc_ownerId;

	/*
	 * 首先，从其 pg_attribute 条目获取列的 ACL
	 */
	fc_attTuple = SearchSysCache2(ATTNUM,
							   ObjectIdGetDatum(fc_table_oid),
							   Int16GetDatum(fc_attnum));
	if (!HeapTupleIsValid(fc_attTuple))
	{
		if (fc_is_missing != NULL)
		{
			/* 返回“没有权限”而不是抛出错误 */
			*fc_is_missing = true;
			return 0;
		}
		else
			ereport(ERROR,
					(errcode(ERRCODE_UNDEFINED_COLUMN),
					 errmsg("attribute %d of relation with OID %u does not exist",
							fc_attnum, fc_table_oid)));
	}

	fc_attributeForm = (Form_pg_attribute) GETSTRUCT(fc_attTuple);

	/* 也检查已删除的列 */
	if (fc_attributeForm->attisdropped)
	{
		if (fc_is_missing != NULL)
		{
			/* 返回“没有权限”而不是抛出错误 */
			*fc_is_missing = true;
			ReleaseSysCache(fc_attTuple);
			return 0;
		}
		else
			ereport(ERROR,
					(errcode(ERRCODE_UNDEFINED_COLUMN),
					 errmsg("attribute %d of relation with OID %u does not exist",
							fc_attnum, fc_table_oid)));
	}

	fc_aclDatum = SysCacheGetAttr(ATTNUM, fc_attTuple, Anum_pg_attribute_attacl,
							   &fc_isNull);

	/*
	 * 在这里我们硬编码有关列的默认 ACL 授予没有
	 * 权限的知识，以便在 attacl 为空的常见情况下
	 * 能够快速返回。
	 */
	if (fc_isNull)
	{
		ReleaseSysCache(fc_attTuple);
		return 0;
	}

	/*
	 * 必须从 pg_class 获取关系的 ownerId。既然我们已经找到
	 * 一个 pg_attribute 条目，唯一可能导致此失败的原因是
	 * 该关系的并发 DROP 自那时起已提交（这只能发生在
	 * 我们没有对该关系的锁时）。我们更愿意报告“没有
	 * 权限”，而不是在这种情况下失败，以避免在 has_column_privilege() 测试中出现不必要的失败。
	 */
	fc_classTuple = SearchSysCache1(RELOID, ObjectIdGetDatum(fc_table_oid));
	if (!HeapTupleIsValid(fc_classTuple))
	{
		ReleaseSysCache(fc_attTuple);
		return 0;
	}
	fc_classForm = (Form_pg_class) GETSTRUCT(fc_classTuple);

	fc_ownerId = fc_classForm->relowner;

	ReleaseSysCache(fc_classTuple);

	/* 如有必要，卸载列的 ACL */
	fc_acl = DatumGetAclP(fc_aclDatum);

	fc_result = aclmask(fc_acl, fc_roleid, fc_ownerId, fc_mask, fc_how);

	/* 如果我们有一个已卸载的副本，释放它 */
	if (fc_acl && (Pointer) fc_acl != DatumGetPointer(fc_aclDatum))
		pfree(fc_acl);

	ReleaseSysCache(fc_attTuple);

	return fc_result;
}

/*
 * 导出的例程，用于检查用户对表的权限
 */
AclMode pg_class_aclmask(Oid fc_table_oid, Oid fc_roleid,
				 AclMode fc_mask, AclMaskHow fc_how)
{
	return pg_class_aclmask_ext(fc_table_oid, fc_roleid, fc_mask, fc_how, NULL);
}

/*
 * 导出的例程，用于检查用户对表的权限
 *
 * 为 pg_class_aclmask() 执行大部分工作，并允许其他
 * 调用者在 is_missing 非 NULL 时避免缺失关系的错误。
 */
AclMode pg_class_aclmask_ext(Oid fc_table_oid, Oid fc_roleid, AclMode fc_mask,
					 AclMaskHow fc_how, bool *fc_is_missing)
{
	AclMode		fc_result;
	HeapTuple	fc_tuple;
	Form_pg_class fc_classForm;
	Datum		fc_aclDatum;
	bool		fc_isNull;
	Acl		   *fc_acl;
	Oid			fc_ownerId;

	/*
	 * 必须从 pg_class 获取关系的元组
	 */
	fc_tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(fc_table_oid));
	if (!HeapTupleIsValid(fc_tuple))
	{
		if (fc_is_missing != NULL)
		{
			/* 返回“没有权限”而不是抛出错误 */
			*fc_is_missing = true;
			return 0;
		}
		else
			ereport(ERROR,
					(errcode(ERRCODE_UNDEFINED_TABLE),
					 errmsg("relation with OID %u does not exist",
							fc_table_oid)));
	}

	fc_classForm = (Form_pg_class) GETSTRUCT(fc_tuple);

	/*
	 * 拒绝任何人更新系统目录的权限，除非
	 * pg_authid.rolsuper 被设置。
	 *
	 * 从 7.4 开始，我们有一些可更新的系统视图；这些不应该
	 * 以这种方式受到保护。假设视图规则可以自行处理。
	 * ACL_USAGE 是如果我们有系统序列时的情况。
	 */
	if ((fc_mask & (ACL_INSERT | ACL_UPDATE | ACL_DELETE | ACL_TRUNCATE | ACL_USAGE)) &&
		IsSystemClass(fc_table_oid, fc_classForm) &&
		fc_classForm->relkind != RELKIND_VIEW &&
		!superuser_arg(fc_roleid))
		fc_mask &= ~(ACL_INSERT | ACL_UPDATE | ACL_DELETE | ACL_TRUNCATE | ACL_USAGE);

	/*
	 * 否则，超级用户绕过所有权限检查。
	 */
	if (superuser_arg(fc_roleid))
	{
		ReleaseSysCache(fc_tuple);
		return fc_mask;
	}

	/*
	 * 正常情况：从 pg_class 获取关系的 ACL
	 */
	fc_ownerId = fc_classForm->relowner;

	fc_aclDatum = SysCacheGetAttr(RELOID, fc_tuple, Anum_pg_class_relacl,
							   &fc_isNull);
	if (fc_isNull)
	{
		/* 没有 ACL，因此构建默认的 ACL */
		switch (fc_classForm->relkind)
		{
			case RELKIND_SEQUENCE:
				fc_acl = acldefault(OBJECT_SEQUENCE, fc_ownerId);
				break;
			default:
				fc_acl = acldefault(OBJECT_TABLE, fc_ownerId);
				break;
		}
		fc_aclDatum = (Datum) 0;
	}
	else
	{
		/* 如果必要，解压 rel 的 ACL */
		fc_acl = DatumGetAclP(fc_aclDatum);
	}

	fc_result = aclmask(fc_acl, fc_roleid, fc_ownerId, fc_mask, fc_how);

	/* 如果我们有一个已卸载的副本，释放它 */
	if (fc_acl && (Pointer) fc_acl != DatumGetPointer(fc_aclDatum))
		pfree(fc_acl);

	ReleaseSysCache(fc_tuple);

	/*
	 * 检查是否在检查 ACL_SELECT，如果是，并且尚未作为结果的一部分设置，则检查用户是否是
	 * pg_read_all_data 角色的成员，该角色允许对所有关系进行读取访问。
	 */
	if (fc_mask & ACL_SELECT && !(fc_result & ACL_SELECT) &&
		has_privs_of_role(fc_roleid, ROLE_PG_READ_ALL_DATA))
		fc_result |= ACL_SELECT;

	/*
	 * 检查是否在检查 ACL_INSERT、ACL_UPDATE 或 ACL_DELETE，如果是，并且尚未作为结果的一部分设置，
	 * 则检查用户是否是 pg_write_all_data 角色的成员，该角色允许对所有关系进行 INSERT/UPDATE/DELETE 访问
	 * （系统目录除外，系统目录需要超级用户权限，见上文）。
	 */
	if (fc_mask & (ACL_INSERT | ACL_UPDATE | ACL_DELETE) &&
		!(fc_result & (ACL_INSERT | ACL_UPDATE | ACL_DELETE)) &&
		has_privs_of_role(fc_roleid, ROLE_PG_WRITE_ALL_DATA))
		fc_result |= (fc_mask & (ACL_INSERT | ACL_UPDATE | ACL_DELETE));

	return fc_result;
}

/*
 * 导出的例程，用于检查用户对数据库的权限
 */
AclMode pg_database_aclmask(Oid fc_db_oid, Oid fc_roleid,
					AclMode fc_mask, AclMaskHow fc_how)
{
	AclMode		fc_result;
	HeapTuple	fc_tuple;
	Datum		fc_aclDatum;
	bool		fc_isNull;
	Acl		   *fc_acl;
	Oid			fc_ownerId;

	/* 超级用户绕过所有权限检查。 */
	if (superuser_arg(fc_roleid))
		return fc_mask;

	/*
	 * 从 pg_database 获取数据库的 ACL
	 */
	fc_tuple = SearchSysCache1(DATABASEOID, ObjectIdGetDatum(fc_db_oid));
	if (!HeapTupleIsValid(fc_tuple))
		ereport(ERROR,
				(errcode(ERRCODE_UNDEFINED_DATABASE),
				 errmsg("database with OID %u does not exist", fc_db_oid)));

	fc_ownerId = ((Form_pg_database) GETSTRUCT(fc_tuple))->datdba;

	fc_aclDatum = SysCacheGetAttr(DATABASEOID, fc_tuple, Anum_pg_database_datacl,
							   &fc_isNull);
	if (fc_isNull)
	{
		/* 没有 ACL，因此构建默认的 ACL */
		fc_acl = acldefault(OBJECT_DATABASE, fc_ownerId);
		fc_aclDatum = (Datum) 0;
	}
	else
	{
		/* 如果必要，解压 ACL */
		fc_acl = DatumGetAclP(fc_aclDatum);
	}

	fc_result = aclmask(fc_acl, fc_roleid, fc_ownerId, fc_mask, fc_how);

	/* 如果我们有一个已卸载的副本，释放它 */
	if (fc_acl && (Pointer) fc_acl != DatumGetPointer(fc_aclDatum))
		pfree(fc_acl);

	ReleaseSysCache(fc_tuple);

	return fc_result;
}

/*
 * 导出的例程，用于检查用户对配置参数 (GUC) 的权限，以 GUC 名称标识。
 */
AclMode pg_parameter_aclmask(const char *fc_name, Oid fc_roleid, AclMode fc_mask, AclMaskHow fc_how)
{
	AclMode		fc_result;
	char	   *fc_parname;
	text	   *fc_partext;
	HeapTuple	fc_tuple;

	/* 超级用户绕过所有权限检查。 */
	if (superuser_arg(fc_roleid))
		return fc_mask;

	/* 将名称转换为其在 pg_parameter_acl 中应有的形式... */
	fc_parname = convert_GUC_name_for_parameter_acl(fc_name);
	fc_partext = cstring_to_text(fc_parname);

	/* ... 然后查找它 */
	fc_tuple = SearchSysCache1(PARAMETERACLNAME, PointerGetDatum(fc_partext));

	if (!HeapTupleIsValid(fc_tuple))
	{
		/* 如果没有条目，GUC 对非超级用户没有权限 */
		fc_result = ACL_NO_RIGHTS;
	}
	else
	{
		Datum		fc_aclDatum;
		bool		fc_isNull;
		Acl		   *fc_acl;

		fc_aclDatum = SysCacheGetAttr(PARAMETERACLNAME, fc_tuple,
								   Anum_pg_parameter_acl_paracl,
								   &fc_isNull);
		if (fc_isNull)
		{
			/* 没有 ACL，因此构建默认的 ACL */
			fc_acl = acldefault(OBJECT_PARAMETER_ACL, BOOTSTRAP_SUPERUSERID);
			fc_aclDatum = (Datum) 0;
		}
		else
		{
			/* 如果必要，解压 ACL */
			fc_acl = DatumGetAclP(fc_aclDatum);
		}

		fc_result = aclmask(fc_acl, fc_roleid, BOOTSTRAP_SUPERUSERID, fc_mask, fc_how);

		/* 如果我们有一个已卸载的副本，释放它 */
		if (fc_acl && (Pointer) fc_acl != DatumGetPointer(fc_aclDatum))
			pfree(fc_acl);

		ReleaseSysCache(fc_tuple);
	}

	pfree(fc_parname);
	pfree(fc_partext);

	return fc_result;
}

/*
 * 导出的例程，用于检查用户对配置参数 (GUC) 的权限，以其 pg_parameter_acl 条目的 OID 标识。
 */
AclMode pg_parameter_acl_aclmask(Oid fc_acl_oid, Oid fc_roleid, AclMode fc_mask, AclMaskHow fc_how)
{
	AclMode		fc_result;
	HeapTuple	fc_tuple;
	Datum		fc_aclDatum;
	bool		fc_isNull;
	Acl		   *fc_acl;

	/* 超级用户绕过所有权限检查。 */
	if (superuser_arg(fc_roleid))
		return fc_mask;

	/* 从 pg_parameter_acl 获取 ACL */
	fc_tuple = SearchSysCache1(PARAMETERACLOID, ObjectIdGetDatum(fc_acl_oid));
	if (!HeapTupleIsValid(fc_tuple))
		ereport(ERROR,
				(errcode(ERRCODE_UNDEFINED_OBJECT),
				 errmsg("parameter ACL with OID %u does not exist",
						fc_acl_oid)));

	fc_aclDatum = SysCacheGetAttr(PARAMETERACLOID, fc_tuple,
							   Anum_pg_parameter_acl_paracl,
							   &fc_isNull);
	if (fc_isNull)
	{
		/* 没有 ACL，因此构建默认的 ACL */
		fc_acl = acldefault(OBJECT_PARAMETER_ACL, BOOTSTRAP_SUPERUSERID);
		fc_aclDatum = (Datum) 0;
	}
	else
	{
		/* 如果必要，解压 ACL */
		fc_acl = DatumGetAclP(fc_aclDatum);
	}

	fc_result = aclmask(fc_acl, fc_roleid, BOOTSTRAP_SUPERUSERID, fc_mask, fc_how);

	/* 如果我们有一个已卸载的副本，释放它 */
	if (fc_acl && (Pointer) fc_acl != DatumGetPointer(fc_aclDatum))
		pfree(fc_acl);

	ReleaseSysCache(fc_tuple);

	return fc_result;
}

/*
 * 导出的例程，用于检查用户对函数的权限
 */
AclMode pg_proc_aclmask(Oid fc_proc_oid, Oid fc_roleid,
				AclMode fc_mask, AclMaskHow fc_how)
{
	AclMode		fc_result;
	HeapTuple	fc_tuple;
	Datum		fc_aclDatum;
	bool		fc_isNull;
	Acl		   *fc_acl;
	Oid			fc_ownerId;

	/* 超级用户绕过所有权限检查。 */
	if (superuser_arg(fc_roleid))
		return fc_mask;

	/*
	 * 从 pg_proc 获取函数的 ACL
	 */
	fc_tuple = SearchSysCache1(PROCOID, ObjectIdGetDatum(fc_proc_oid));
	if (!HeapTupleIsValid(fc_tuple))
		ereport(ERROR,
				(errcode(ERRCODE_UNDEFINED_FUNCTION),
				 errmsg("function with OID %u does not exist", fc_proc_oid)));

	fc_ownerId = ((Form_pg_proc) GETSTRUCT(fc_tuple))->proowner;

	fc_aclDatum = SysCacheGetAttr(PROCOID, fc_tuple, Anum_pg_proc_proacl,
							   &fc_isNull);
	if (fc_isNull)
	{
		/* 没有 ACL，因此构建默认的 ACL */
		fc_acl = acldefault(OBJECT_FUNCTION, fc_ownerId);
		fc_aclDatum = (Datum) 0;
	}
	else
	{
		/* 如果必要，解压 ACL */
		fc_acl = DatumGetAclP(fc_aclDatum);
	}

	fc_result = aclmask(fc_acl, fc_roleid, fc_ownerId, fc_mask, fc_how);

	/* 如果我们有一个已卸载的副本，释放它 */
	if (fc_acl && (Pointer) fc_acl != DatumGetPointer(fc_aclDatum))
		pfree(fc_acl);

	ReleaseSysCache(fc_tuple);

	return fc_result;
}

/*
 * 导出的例程，用于检查用户对语言的权限
 */
AclMode pg_language_aclmask(Oid fc_lang_oid, Oid fc_roleid,
					AclMode fc_mask, AclMaskHow fc_how)
{
	AclMode		fc_result;
	HeapTuple	fc_tuple;
	Datum		fc_aclDatum;
	bool		fc_isNull;
	Acl		   *fc_acl;
	Oid			fc_ownerId;

	/* 超级用户绕过所有权限检查。 */
	if (superuser_arg(fc_roleid))
		return fc_mask;

	/*
	 * 从 pg_language 获取语言的 ACL
	 */
	fc_tuple = SearchSysCache1(LANGOID, ObjectIdGetDatum(fc_lang_oid));
	if (!HeapTupleIsValid(fc_tuple))
		ereport(ERROR,
				(errcode(ERRCODE_UNDEFINED_OBJECT),
				 errmsg("language with OID %u does not exist", fc_lang_oid)));

	fc_ownerId = ((Form_pg_language) GETSTRUCT(fc_tuple))->lanowner;

	fc_aclDatum = SysCacheGetAttr(LANGOID, fc_tuple, Anum_pg_language_lanacl,
							   &fc_isNull);
	if (fc_isNull)
	{
		/* 没有 ACL，因此构建默认的 ACL */
		fc_acl = acldefault(OBJECT_LANGUAGE, fc_ownerId);
		fc_aclDatum = (Datum) 0;
	}
	else
	{
		/* 如果必要，解压 ACL */
		fc_acl = DatumGetAclP(fc_aclDatum);
	}

	fc_result = aclmask(fc_acl, fc_roleid, fc_ownerId, fc_mask, fc_how);

	/* 如果我们有一个已卸载的副本，释放它 */
	if (fc_acl && (Pointer) fc_acl != DatumGetPointer(fc_aclDatum))
		pfree(fc_acl);

	ReleaseSysCache(fc_tuple);

	return fc_result;
}

/*
 * 导出的例程，用于检查用户对大型对象的权限
 *
 * 当大型对象被打开以进行读取时，它是相对于调用者的快照打开的，
 * 但当它被打开以进行写入时，将使用当前的 MVCC 快照。请参见 doc/src/sgml/lobj.sgml。
 * 此函数接受一个快照参数，以便权限检查可以相对于将用于读取底层
 * 数据的相同快照进行。调用者实际上将传递 NULL 以获取瞬时的 MVCC
 * 快照，因为我们对快照参数所做的只是将其传递给 systable_beginscan()。
 */
AclMode pg_largeobject_aclmask_snapshot(Oid fc_lobj_oid, Oid fc_roleid,
								AclMode fc_mask, AclMaskHow fc_how,
								Snapshot fc_snapshot)
{
	AclMode		fc_result;
	Relation	pg_lo_meta;
	ScanKeyData fc_entry[1];
	SysScanDesc fc_scan;
	HeapTuple	fc_tuple;
	Datum		fc_aclDatum;
	bool		fc_isNull;
	Acl		   *fc_acl;
	Oid			fc_ownerId;

	/* 超级用户绕过所有权限检查。 */
	if (superuser_arg(fc_roleid))
		return fc_mask;

	/*
	 * 从 pg_largeobject_metadata 获取大型对象的 ACL
	 */
	pg_lo_meta = table_open(LargeObjectMetadataRelationId,
							AccessShareLock);

	ScanKeyInit(&fc_entry[0],
				Anum_pg_largeobject_metadata_oid,
				BTEqualStrategyNumber, F_OIDEQ,
				ObjectIdGetDatum(fc_lobj_oid));

	fc_scan = systable_beginscan(pg_lo_meta,
							  LargeObjectMetadataOidIndexId, true,
							  fc_snapshot, 1, fc_entry);

	fc_tuple = systable_getnext(fc_scan);
	if (!HeapTupleIsValid(fc_tuple))
		ereport(ERROR,
				(errcode(ERRCODE_UNDEFINED_OBJECT),
				 errmsg("large object %u does not exist", fc_lobj_oid)));

	fc_ownerId = ((Form_pg_largeobject_metadata) GETSTRUCT(fc_tuple))->lomowner;

	fc_aclDatum = heap_getattr(fc_tuple, Anum_pg_largeobject_metadata_lomacl,
							RelationGetDescr(pg_lo_meta), &fc_isNull);

	if (fc_isNull)
	{
		/* 没有 ACL，因此构建默认的 ACL */
		fc_acl = acldefault(OBJECT_LARGEOBJECT, fc_ownerId);
		fc_aclDatum = (Datum) 0;
	}
	else
	{
		/* 如果必要，解压 ACL */
		fc_acl = DatumGetAclP(fc_aclDatum);
	}

	fc_result = aclmask(fc_acl, fc_roleid, fc_ownerId, fc_mask, fc_how);

	/* 如果我们有一个已卸载的副本，释放它 */
	if (fc_acl && (Pointer) fc_acl != DatumGetPointer(fc_aclDatum))
		pfree(fc_acl);

	systable_endscan(fc_scan);

	table_close(pg_lo_meta, AccessShareLock);

	return fc_result;
}

/*
 * 导出的例程，用于检查用户对命名空间的权限
 */
AclMode pg_namespace_aclmask(Oid fc_nsp_oid, Oid fc_roleid,
					 AclMode fc_mask, AclMaskHow fc_how)
{
	AclMode		fc_result;
	HeapTuple	fc_tuple;
	Datum		fc_aclDatum;
	bool		fc_isNull;
	Acl		   *fc_acl;
	Oid			fc_ownerId;

	/* 超级用户绕过所有权限检查。 */
	if (superuser_arg(fc_roleid))
		return fc_mask;

	/*
	 * 如果我们已将此命名空间分配为临时命名空间，请检查以确保我们对数据库具有 CREATE TEMP 权限，
	 * 如果是，则表现得好像我们对命名空间具有所有标准权限（但没有 GRANT OPTION）。
	 * 如果我们没有 CREATE TEMP 权限，则表现得好像我们只有 USAGE 权限（而没有 CREATE 权限）。
	 *
	 * 鉴于在 InitTempTableNamespace 中的检查，这看起来可能是多余的，但实际上并不是,
	 * 因为当前用户 ID 可能在那之后已发生更改。这种行为的结果是，具有 SECURITY DEFINER 的函数可以创建
	 * 临时表，然后在同一会话中按权限访问这些表，即使该会话没有创建临时表的权限。
	 *
	 * XXX 在 InitTempTableNamespace 中报告特别错误消息是否安全？返回零意味着我们将得到一个
	 * 普通的 "schema pg_temp_N 的权限被拒绝" 消息，这并不是特别用户友好。
	 */
	if (isTempNamespace(fc_nsp_oid))
	{
		if (pg_database_aclcheck(MyDatabaseId, fc_roleid,
								 ACL_CREATE_TEMP) == ACLCHECK_OK)
			return fc_mask & ACL_ALL_RIGHTS_SCHEMA;
		else
			return fc_mask & ACL_USAGE;
	}

	/*
	 * 从 pg_namespace 获取 schema 的 ACL
	 */
	fc_tuple = SearchSysCache1(NAMESPACEOID, ObjectIdGetDatum(fc_nsp_oid));
	if (!HeapTupleIsValid(fc_tuple))
		ereport(ERROR,
				(errcode(ERRCODE_UNDEFINED_SCHEMA),
				 errmsg("schema with OID %u does not exist", fc_nsp_oid)));

	fc_ownerId = ((Form_pg_namespace) GETSTRUCT(fc_tuple))->nspowner;

	fc_aclDatum = SysCacheGetAttr(NAMESPACEOID, fc_tuple, Anum_pg_namespace_nspacl,
							   &fc_isNull);
	if (fc_isNull)
	{
		/* 没有 ACL，因此构建默认的 ACL */
		fc_acl = acldefault(OBJECT_SCHEMA, fc_ownerId);
		fc_aclDatum = (Datum) 0;
	}
	else
	{
		/* 如果必要，解压 ACL */
		fc_acl = DatumGetAclP(fc_aclDatum);
	}

	fc_result = aclmask(fc_acl, fc_roleid, fc_ownerId, fc_mask, fc_how);

	/* 如果我们有一个已卸载的副本，释放它 */
	if (fc_acl && (Pointer) fc_acl != DatumGetPointer(fc_aclDatum))
		pfree(fc_acl);

	ReleaseSysCache(fc_tuple);

	/*
	 * 检查是否在检查 ACL_USAGE，如果是，并且尚未作为结果的一部分设置，则检查用户是否是
	 * pg_read_all_data 或 pg_write_all_data 角色的成员，这些角色允许对所有模式进行使用访问。
	 */
	if (fc_mask & ACL_USAGE && !(fc_result & ACL_USAGE) &&
		(has_privs_of_role(fc_roleid, ROLE_PG_READ_ALL_DATA) ||
		 has_privs_of_role(fc_roleid, ROLE_PG_WRITE_ALL_DATA)))
		fc_result |= ACL_USAGE;
	return fc_result;
}

/*
 * 导出的例程，用于检查用户对表空间的权限
 */
AclMode pg_tablespace_aclmask(Oid fc_spc_oid, Oid fc_roleid,
					  AclMode fc_mask, AclMaskHow fc_how)
{
	AclMode		fc_result;
	HeapTuple	fc_tuple;
	Datum		fc_aclDatum;
	bool		fc_isNull;
	Acl		   *fc_acl;
	Oid			fc_ownerId;

	/* 超级用户绕过所有权限检查。 */
	if (superuser_arg(fc_roleid))
		return fc_mask;

	/*
	 * 从 pg_tablespace 获取表空间的 ACL
	 */
	fc_tuple = SearchSysCache1(TABLESPACEOID, ObjectIdGetDatum(fc_spc_oid));
	if (!HeapTupleIsValid(fc_tuple))
		ereport(ERROR,
				(errcode(ERRCODE_UNDEFINED_OBJECT),
				 errmsg("tablespace with OID %u does not exist", fc_spc_oid)));

	fc_ownerId = ((Form_pg_tablespace) GETSTRUCT(fc_tuple))->spcowner;

	fc_aclDatum = SysCacheGetAttr(TABLESPACEOID, fc_tuple,
							   Anum_pg_tablespace_spcacl,
							   &fc_isNull);

	if (fc_isNull)
	{
		/* 没有 ACL，因此构建默认的 ACL */
		fc_acl = acldefault(OBJECT_TABLESPACE, fc_ownerId);
		fc_aclDatum = (Datum) 0;
	}
	else
	{
		/* 如果必要，解压 ACL */
		fc_acl = DatumGetAclP(fc_aclDatum);
	}

	fc_result = aclmask(fc_acl, fc_roleid, fc_ownerId, fc_mask, fc_how);

	/* 如果我们有一个已卸载的副本，释放它 */
	if (fc_acl && (Pointer) fc_acl != DatumGetPointer(fc_aclDatum))
		pfree(fc_acl);

	ReleaseSysCache(fc_tuple);

	return fc_result;
}

/*
 * 导出的例程，用于检查用户对外部数据包装器的权限
 */
AclMode pg_foreign_data_wrapper_aclmask(Oid fc_fdw_oid, Oid fc_roleid,
								AclMode fc_mask, AclMaskHow fc_how)
{
	AclMode		fc_result;
	HeapTuple	fc_tuple;
	Datum		fc_aclDatum;
	bool		fc_isNull;
	Acl		   *fc_acl;
	Oid			fc_ownerId;

	Form_pg_foreign_data_wrapper fc_fdwForm;

	/* 绕过超级用户的权限检查 */
	if (superuser_arg(fc_roleid))
		return fc_mask;

	/*
	 * 必须从 pg_foreign_data_wrapper 获取 FDW 的元组
	 */
	fc_tuple = SearchSysCache1(FOREIGNDATAWRAPPEROID, ObjectIdGetDatum(fc_fdw_oid));
	if (!HeapTupleIsValid(fc_tuple))
		ereport(ERROR,
				(errcode(ERRCODE_UNDEFINED_OBJECT),
				 errmsg("foreign-data wrapper with OID %u does not exist",
						fc_fdw_oid)));
	fc_fdwForm = (Form_pg_foreign_data_wrapper) GETSTRUCT(fc_tuple);

	/*
	 * 正常情况：从 pg_foreign_data_wrapper 获取 FDW 的 ACL
	 */
	fc_ownerId = fc_fdwForm->fdwowner;

	fc_aclDatum = SysCacheGetAttr(FOREIGNDATAWRAPPEROID, fc_tuple,
							   Anum_pg_foreign_data_wrapper_fdwacl, &fc_isNull);
	if (fc_isNull)
	{
		/* 没有 ACL，因此构建默认的 ACL */
		fc_acl = acldefault(OBJECT_FDW, fc_ownerId);
		fc_aclDatum = (Datum) 0;
	}
	else
	{
		/* 如果必要，解压 rel 的 ACL */
		fc_acl = DatumGetAclP(fc_aclDatum);
	}

	fc_result = aclmask(fc_acl, fc_roleid, fc_ownerId, fc_mask, fc_how);

	/* 如果我们有一个已卸载的副本，释放它 */
	if (fc_acl && (Pointer) fc_acl != DatumGetPointer(fc_aclDatum))
		pfree(fc_acl);

	ReleaseSysCache(fc_tuple);

	return fc_result;
}

/*
 * 导出的例程，用于检查用户对外部服务器的权限。
 */
AclMode pg_foreign_server_aclmask(Oid fc_srv_oid, Oid fc_roleid,
						  AclMode fc_mask, AclMaskHow fc_how)
{
	AclMode		fc_result;
	HeapTuple	fc_tuple;
	Datum		fc_aclDatum;
	bool		fc_isNull;
	Acl		   *fc_acl;
	Oid			fc_ownerId;

	Form_pg_foreign_server fc_srvForm;

	/* 绕过超级用户的权限检查 */
	if (superuser_arg(fc_roleid))
		return fc_mask;

	/*
	 * 必须从 pg_foreign_data_wrapper 获取 FDW 的元组
	 */
	fc_tuple = SearchSysCache1(FOREIGNSERVEROID, ObjectIdGetDatum(fc_srv_oid));
	if (!HeapTupleIsValid(fc_tuple))
		ereport(ERROR,
				(errcode(ERRCODE_UNDEFINED_OBJECT),
				 errmsg("foreign server with OID %u does not exist",
						fc_srv_oid)));
	fc_srvForm = (Form_pg_foreign_server) GETSTRUCT(fc_tuple);

	/*
	 * 正常情况：从 pg_foreign_server 获取外部服务器的 ACL
	 */
	fc_ownerId = fc_srvForm->srvowner;

	fc_aclDatum = SysCacheGetAttr(FOREIGNSERVEROID, fc_tuple,
							   Anum_pg_foreign_server_srvacl, &fc_isNull);
	if (fc_isNull)
	{
		/* 没有 ACL，因此构建默认的 ACL */
		fc_acl = acldefault(OBJECT_FOREIGN_SERVER, fc_ownerId);
		fc_aclDatum = (Datum) 0;
	}
	else
	{
		/* 如果必要，解压 rel 的 ACL */
		fc_acl = DatumGetAclP(fc_aclDatum);
	}

	fc_result = aclmask(fc_acl, fc_roleid, fc_ownerId, fc_mask, fc_how);

	/* 如果我们有一个已卸载的副本，释放它 */
	if (fc_acl && (Pointer) fc_acl != DatumGetPointer(fc_aclDatum))
		pfree(fc_acl);

	ReleaseSysCache(fc_tuple);

	return fc_result;
}

/*
 * 导出的例程，用于检查用户对一个类型的权限。
 */
AclMode pg_type_aclmask(Oid fc_type_oid, Oid fc_roleid, AclMode fc_mask, AclMaskHow fc_how)
{
	AclMode		fc_result;
	HeapTuple	fc_tuple;
	Datum		fc_aclDatum;
	bool		fc_isNull;
	Acl		   *fc_acl;
	Oid			fc_ownerId;

	Form_pg_type fc_typeForm;

	/* 绕过超级用户的权限检查 */
	if (superuser_arg(fc_roleid))
		return fc_mask;

	/*
	 * 必须从 pg_type 获取类型的元组
	 */
	fc_tuple = SearchSysCache1(TYPEOID, ObjectIdGetDatum(fc_type_oid));
	if (!HeapTupleIsValid(fc_tuple))
		ereport(ERROR,
				(errcode(ERRCODE_UNDEFINED_OBJECT),
				 errmsg("type with OID %u does not exist",
						fc_type_oid)));
	fc_typeForm = (Form_pg_type) GETSTRUCT(fc_tuple);

	/*
	 * “真正的”数组类型不管理自己的权限；请咨询元素类型。
	 */
	if (IsTrueArrayType(fc_typeForm))
	{
		Oid			fc_elttype_oid = fc_typeForm->typelem;

		ReleaseSysCache(fc_tuple);

		fc_tuple = SearchSysCache1(TYPEOID, ObjectIdGetDatum(fc_elttype_oid));
		/* 该情况不是用户可见的错误，因此 elog 而不是 ereport */
		if (!HeapTupleIsValid(fc_tuple))
			elog(ERROR, "cache lookup failed for type %u", fc_elttype_oid);
		fc_typeForm = (Form_pg_type) GETSTRUCT(fc_tuple);
	}

	/*
	 * 现在从元组中获取类型的所有者和 ACL
	 */
	fc_ownerId = fc_typeForm->typowner;

	fc_aclDatum = SysCacheGetAttr(TYPEOID, fc_tuple,
							   Anum_pg_type_typacl, &fc_isNull);
	if (fc_isNull)
	{
		/* 没有 ACL，因此构建默认的 ACL */
		fc_acl = acldefault(OBJECT_TYPE, fc_ownerId);
		fc_aclDatum = (Datum) 0;
	}
	else
	{
		/* 如果必要，解压 rel 的 ACL */
		fc_acl = DatumGetAclP(fc_aclDatum);
	}

	fc_result = aclmask(fc_acl, fc_roleid, fc_ownerId, fc_mask, fc_how);

	/* 如果我们有一个已卸载的副本，释放它 */
	if (fc_acl && (Pointer) fc_acl != DatumGetPointer(fc_aclDatum))
		pfree(fc_acl);

	ReleaseSysCache(fc_tuple);

	return fc_result;
}

/*
 * 导出的例程，用于检查用户对列的访问权限
 *
 * 如果用户拥有由 'mode' 标识的任何权限，则返回 ACLCHECK_OK；
 * 否则返回适当的错误代码（在实践中，始终为
 * ACLCHECK_NO_PRIV）。
 *
 * 与 pg_attribute_aclmask 一样，仅考虑直接授予列的权限。
 */
AclResult pg_attribute_aclcheck(Oid fc_table_oid, AttrNumber fc_attnum,
					  Oid fc_roleid, AclMode fc_mode)
{
	return pg_attribute_aclcheck_ext(fc_table_oid, fc_attnum, fc_roleid, fc_mode, NULL);
}


/*
 * 导出的例程，用于检查用户对列的访问权限
 *
 * 为 pg_attribute_aclcheck() 处理大部分工作，并允许其他
 * 调用者在 is_missing 非 NULL 时避免缺少属性 ERROR。
 */
AclResult pg_attribute_aclcheck_ext(Oid fc_table_oid, AttrNumber fc_attnum,
						  Oid fc_roleid, AclMode fc_mode, bool *fc_is_missing)
{
	if (pg_attribute_aclmask_ext(fc_table_oid, fc_attnum, fc_roleid, fc_mode,
								 ACLMASK_ANY, fc_is_missing) != 0)
		return ACLCHECK_OK;
	else
		return ACLCHECK_NO_PRIV;
}

/*
 * 导出的例程，用于检查用户对任何/所有列的访问权限
 *
 * 如果 'how' 为 ACLMASK_ANY，则如果用户在关系中的任何未删除列上拥有由 'mode' 标识的任何权限，则返回 ACLCHECK_OK；
 * 否则返回适当的错误代码（在实践中，始终为
 * ACLCHECK_NO_PRIV）。
 *
 * 如果 'how' 为 ACLMASK_ALL，则如果用户在关系中的每个未删除列上拥有由 'mode' 标识的任何权限（并且必须至少有一个这样的列），则返回 ACLCHECK_OK；
 * 否则返回适当的错误代码（在实践中，始终为 ACLCHECK_NO_PRIV）。
 *
 * 与 pg_attribute_aclmask 一样，仅考虑直接授予列的权限。
 *
 * 注意：此处不考虑系统列；在某些情况下，这可能是合适的，但在某些情况下则不然。
 */
AclResult pg_attribute_aclcheck_all(Oid fc_table_oid, Oid fc_roleid, AclMode fc_mode,
						  AclMaskHow fc_how)
{
	AclResult	fc_result;
	HeapTuple	fc_classTuple;
	Form_pg_class fc_classForm;
	AttrNumber	fc_nattrs;
	AttrNumber	fc_curr_att;

	/*
	 * 必须获取 pg_class 行以检查属性数量。与
	 * pg_attribute_aclmask 一样，如果我们遇到任何意外的查找错误，
	 * 我们更倾向于返回“没有权限”，而不是抛出错误。
	 */
	fc_classTuple = SearchSysCache1(RELOID, ObjectIdGetDatum(fc_table_oid));
	if (!HeapTupleIsValid(fc_classTuple))
		return ACLCHECK_NO_PRIV;
	fc_classForm = (Form_pg_class) GETSTRUCT(fc_classTuple);

	fc_nattrs = fc_classForm->relnatts;

	ReleaseSysCache(fc_classTuple);

	/*
	 * 初始化结果，以防没有未删除的列。我们希望
	 * 报告失败以应对 'how' 的任何值。
	 */
	fc_result = ACLCHECK_NO_PRIV;

	for (fc_curr_att = 1; fc_curr_att <= fc_nattrs; fc_curr_att++)
	{
		HeapTuple	fc_attTuple;
		AclMode		fc_attmask;

		fc_attTuple = SearchSysCache2(ATTNUM,
								   ObjectIdGetDatum(fc_table_oid),
								   Int16GetDatum(fc_curr_att));
		if (!HeapTupleIsValid(fc_attTuple))
			continue;

		/* 忽略已删除的列 */
		if (((Form_pg_attribute) GETSTRUCT(fc_attTuple))->attisdropped)
		{
			ReleaseSysCache(fc_attTuple);
			continue;
		}

		/*
		 * 这里我们硬编码默认的列 ACL 不授予任何权限，
		 * 以便在 attacl 为 NULL 的非常常见情况下快速退出。
		 */
		if (heap_attisnull(fc_attTuple, Anum_pg_attribute_attacl, NULL))
			fc_attmask = 0;
		else
			fc_attmask = pg_attribute_aclmask(fc_table_oid, fc_curr_att, fc_roleid,
										   fc_mode, ACLMASK_ANY);

		ReleaseSysCache(fc_attTuple);

		if (fc_attmask != 0)
		{
			fc_result = ACLCHECK_OK;
			if (fc_how == ACLMASK_ANY)
				break;			/* 成功时任何成功 */
		}
		else
		{
			fc_result = ACLCHECK_NO_PRIV;
			if (fc_how == ACLMASK_ALL)
				break;			/* 失败时任何失败 */
		}
	}

	return fc_result;
}

/*
 * 导出的例程，用于检查用户对表的访问权限
 *
 * 如果用户拥有由 'mode' 标识的任何权限，则返回 ACLCHECK_OK；
 * 否则返回适当的错误代码（在实践中，始终为
 * ACLCHECK_NO_PRIV）。
 */
AclResult pg_class_aclcheck(Oid fc_table_oid, Oid fc_roleid, AclMode fc_mode)
{
	return pg_class_aclcheck_ext(fc_table_oid, fc_roleid, fc_mode, NULL);
}

/*
 * 导出的例程，用于检查用户对表的访问权限
 *
 * 为 pg_class_aclcheck() 处理大部分工作，并允许其他
 * 调用者在 is_missing 非 NULL 时避免缺少关系 ERROR。
 */
AclResult pg_class_aclcheck_ext(Oid fc_table_oid, Oid fc_roleid,
					  AclMode fc_mode, bool *fc_is_missing)
{
	if (pg_class_aclmask_ext(fc_table_oid, fc_roleid, fc_mode,
							 ACLMASK_ANY, fc_is_missing) != 0)
		return ACLCHECK_OK;
	else
		return ACLCHECK_NO_PRIV;
}

/*
 * 导出的例程，用于检查用户对数据库的访问权限
 */
AclResult pg_database_aclcheck(Oid fc_db_oid, Oid fc_roleid, AclMode fc_mode)
{
	if (pg_database_aclmask(fc_db_oid, fc_roleid, fc_mode, ACLMASK_ANY) != 0)
		return ACLCHECK_OK;
	else
		return ACLCHECK_NO_PRIV;
}

/*
 * 导出的例程，用于检查用户对配置参数（GUC）的访问权限，
 * 由 GUC 名称标识。
 */
AclResult pg_parameter_aclcheck(const char *fc_name, Oid fc_roleid, AclMode fc_mode)
{
	if (pg_parameter_aclmask(fc_name, fc_roleid, fc_mode, ACLMASK_ANY) != 0)
		return ACLCHECK_OK;
	else
		return ACLCHECK_NO_PRIV;
}

/*
 * 导出的例程，用于检查用户对配置参数（GUC）的访问权限，
 * 由其 pg_parameter_acl 条目的 OID 标识。
 */
AclResult pg_parameter_acl_aclcheck(Oid fc_acl_oid, Oid fc_roleid, AclMode fc_mode)
{
	if (pg_parameter_acl_aclmask(fc_acl_oid, fc_roleid, fc_mode, ACLMASK_ANY) != 0)
		return ACLCHECK_OK;
	else
		return ACLCHECK_NO_PRIV;
}

/*
 * 导出的例程，用于检查用户对函数的访问权限
 */
AclResult pg_proc_aclcheck(Oid fc_proc_oid, Oid fc_roleid, AclMode fc_mode)
{
	if (pg_proc_aclmask(fc_proc_oid, fc_roleid, fc_mode, ACLMASK_ANY) != 0)
		return ACLCHECK_OK;
	else
		return ACLCHECK_NO_PRIV;
}

/*
 * 导出的例程，用于检查用户对语言的访问权限
 */
AclResult pg_language_aclcheck(Oid fc_lang_oid, Oid fc_roleid, AclMode fc_mode)
{
	if (pg_language_aclmask(fc_lang_oid, fc_roleid, fc_mode, ACLMASK_ANY) != 0)
		return ACLCHECK_OK;
	else
		return ACLCHECK_NO_PRIV;
}

/*
 * 导出的例程，用于检查用户对大对象的访问权限
 */
AclResult pg_largeobject_aclcheck_snapshot(Oid fc_lobj_oid, Oid fc_roleid, AclMode fc_mode,
								 Snapshot fc_snapshot)
{
	if (pg_largeobject_aclmask_snapshot(fc_lobj_oid, fc_roleid, fc_mode,
										ACLMASK_ANY, fc_snapshot) != 0)
		return ACLCHECK_OK;
	else
		return ACLCHECK_NO_PRIV;
}

/*
 * 导出的例程，用于检查用户对命名空间的访问权限
 */
AclResult pg_namespace_aclcheck(Oid fc_nsp_oid, Oid fc_roleid, AclMode fc_mode)
{
	if (pg_namespace_aclmask(fc_nsp_oid, fc_roleid, fc_mode, ACLMASK_ANY) != 0)
		return ACLCHECK_OK;
	else
		return ACLCHECK_NO_PRIV;
}

/*
 * 导出的例程，用于检查用户对表空间的访问权限
 */
AclResult pg_tablespace_aclcheck(Oid fc_spc_oid, Oid fc_roleid, AclMode fc_mode)
{
	if (pg_tablespace_aclmask(fc_spc_oid, fc_roleid, fc_mode, ACLMASK_ANY) != 0)
		return ACLCHECK_OK;
	else
		return ACLCHECK_NO_PRIV;
}

/*
 * 导出的例程，用于检查用户对外部数据包装器的访问权限
 */
AclResult pg_foreign_data_wrapper_aclcheck(Oid fc_fdw_oid, Oid fc_roleid, AclMode fc_mode)
{
	if (pg_foreign_data_wrapper_aclmask(fc_fdw_oid, fc_roleid, fc_mode, ACLMASK_ANY) != 0)
		return ACLCHECK_OK;
	else
		return ACLCHECK_NO_PRIV;
}

/*
 * 导出的例程，用于检查用户对外部服务器的访问权限
 */
AclResult pg_foreign_server_aclcheck(Oid fc_srv_oid, Oid fc_roleid, AclMode fc_mode)
{
	if (pg_foreign_server_aclmask(fc_srv_oid, fc_roleid, fc_mode, ACLMASK_ANY) != 0)
		return ACLCHECK_OK;
	else
		return ACLCHECK_NO_PRIV;
}

/*
 * 导出的例程，用于检查用户对类型的访问权限
 */
AclResult pg_type_aclcheck(Oid fc_type_oid, Oid fc_roleid, AclMode fc_mode)
{
	if (pg_type_aclmask(fc_type_oid, fc_roleid, fc_mode, ACLMASK_ANY) != 0)
		return ACLCHECK_OK;
	else
		return ACLCHECK_NO_PRIV;
}

/*
 * 对指定OID的关系进行所有权检查。
 */
bool pg_class_ownercheck(Oid fc_class_oid, Oid fc_roleid)
{
	HeapTuple	fc_tuple;
	Oid			fc_ownerId;

	/* 超级用户绕过所有权限检查。 */
	if (superuser_arg(fc_roleid))
		return true;

	fc_tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(fc_class_oid));
	if (!HeapTupleIsValid(fc_tuple))
		ereport(ERROR,
				(errcode(ERRCODE_UNDEFINED_TABLE),
				 errmsg("relation with OID %u does not exist", fc_class_oid)));

	fc_ownerId = ((Form_pg_class) GETSTRUCT(fc_tuple))->relowner;

	ReleaseSysCache(fc_tuple);

	return has_privs_of_role(fc_roleid, fc_ownerId);
}

/*
 * 对指定OID的类型进行所有权检查。
 */
bool pg_type_ownercheck(Oid fc_type_oid, Oid fc_roleid)
{
	HeapTuple	fc_tuple;
	Oid			fc_ownerId;

	/* 超级用户绕过所有权限检查。 */
	if (superuser_arg(fc_roleid))
		return true;

	fc_tuple = SearchSysCache1(TYPEOID, ObjectIdGetDatum(fc_type_oid));
	if (!HeapTupleIsValid(fc_tuple))
		ereport(ERROR,
				(errcode(ERRCODE_UNDEFINED_OBJECT),
				 errmsg("type with OID %u does not exist", fc_type_oid)));

	fc_ownerId = ((Form_pg_type) GETSTRUCT(fc_tuple))->typowner;

	ReleaseSysCache(fc_tuple);

	return has_privs_of_role(fc_roleid, fc_ownerId);
}

/*
 * 对指定OID的运算符进行所有权检查。
 */
bool pg_oper_ownercheck(Oid fc_oper_oid, Oid fc_roleid)
{
	HeapTuple	fc_tuple;
	Oid			fc_ownerId;

	/* 超级用户绕过所有权限检查。 */
	if (superuser_arg(fc_roleid))
		return true;

	fc_tuple = SearchSysCache1(OPEROID, ObjectIdGetDatum(fc_oper_oid));
	if (!HeapTupleIsValid(fc_tuple))
		ereport(ERROR,
				(errcode(ERRCODE_UNDEFINED_FUNCTION),
				 errmsg("operator with OID %u does not exist", fc_oper_oid)));

	fc_ownerId = ((Form_pg_operator) GETSTRUCT(fc_tuple))->oprowner;

	ReleaseSysCache(fc_tuple);

	return has_privs_of_role(fc_roleid, fc_ownerId);
}

/*
 * 对指定OID的函数进行所有权检查。
 */
bool pg_proc_ownercheck(Oid fc_proc_oid, Oid fc_roleid)
{
	HeapTuple	fc_tuple;
	Oid			fc_ownerId;

	/* 超级用户绕过所有权限检查。 */
	if (superuser_arg(fc_roleid))
		return true;

	fc_tuple = SearchSysCache1(PROCOID, ObjectIdGetDatum(fc_proc_oid));
	if (!HeapTupleIsValid(fc_tuple))
		ereport(ERROR,
				(errcode(ERRCODE_UNDEFINED_FUNCTION),
				 errmsg("function with OID %u does not exist", fc_proc_oid)));

	fc_ownerId = ((Form_pg_proc) GETSTRUCT(fc_tuple))->proowner;

	ReleaseSysCache(fc_tuple);

	return has_privs_of_role(fc_roleid, fc_ownerId);
}

/*
 * 对指定OID的过程语言进行所有权检查
 */
bool pg_language_ownercheck(Oid fc_lan_oid, Oid fc_roleid)
{
	HeapTuple	fc_tuple;
	Oid			fc_ownerId;

	/* 超级用户绕过所有权限检查。 */
	if (superuser_arg(fc_roleid))
		return true;

	fc_tuple = SearchSysCache1(LANGOID, ObjectIdGetDatum(fc_lan_oid));
	if (!HeapTupleIsValid(fc_tuple))
		ereport(ERROR,
				(errcode(ERRCODE_UNDEFINED_FUNCTION),
				 errmsg("language with OID %u does not exist", fc_lan_oid)));

	fc_ownerId = ((Form_pg_language) GETSTRUCT(fc_tuple))->lanowner;

	ReleaseSysCache(fc_tuple);

	return has_privs_of_role(fc_roleid, fc_ownerId);
}

/*
 * 对指定OID的大对象进行所有权检查
 *
 * 这仅用于像ALTER LARGE OBJECT这样的操作，它们始终
 * 与最新快照相对。
 */
bool pg_largeobject_ownercheck(Oid fc_lobj_oid, Oid fc_roleid)
{
	Relation	pg_lo_meta;
	ScanKeyData fc_entry[1];
	SysScanDesc fc_scan;
	HeapTuple	fc_tuple;
	Oid			fc_ownerId;

	/* 超级用户绕过所有权限检查。 */
	if (superuser_arg(fc_roleid))
		return true;

	/* pg_largeobject_metadata 没有 syscache */
	pg_lo_meta = table_open(LargeObjectMetadataRelationId,
							AccessShareLock);

	ScanKeyInit(&fc_entry[0],
				Anum_pg_largeobject_metadata_oid,
				BTEqualStrategyNumber, F_OIDEQ,
				ObjectIdGetDatum(fc_lobj_oid));

	fc_scan = systable_beginscan(pg_lo_meta,
							  LargeObjectMetadataOidIndexId, true,
							  NULL, 1, fc_entry);

	fc_tuple = systable_getnext(fc_scan);
	if (!HeapTupleIsValid(fc_tuple))
		ereport(ERROR,
				(errcode(ERRCODE_UNDEFINED_OBJECT),
				 errmsg("large object %u does not exist", fc_lobj_oid)));

	fc_ownerId = ((Form_pg_largeobject_metadata) GETSTRUCT(fc_tuple))->lomowner;

	systable_endscan(fc_scan);
	table_close(pg_lo_meta, AccessShareLock);

	return has_privs_of_role(fc_roleid, fc_ownerId);
}

/*
 * 对指定OID的命名空间进行所有权检查。
 */
bool pg_namespace_ownercheck(Oid fc_nsp_oid, Oid fc_roleid)
{
	HeapTuple	fc_tuple;
	Oid			fc_ownerId;

	/* 超级用户绕过所有权限检查。 */
	if (superuser_arg(fc_roleid))
		return true;

	fc_tuple = SearchSysCache1(NAMESPACEOID, ObjectIdGetDatum(fc_nsp_oid));
	if (!HeapTupleIsValid(fc_tuple))
		ereport(ERROR,
				(errcode(ERRCODE_UNDEFINED_SCHEMA),
				 errmsg("schema with OID %u does not exist", fc_nsp_oid)));

	fc_ownerId = ((Form_pg_namespace) GETSTRUCT(fc_tuple))->nspowner;

	ReleaseSysCache(fc_tuple);

	return has_privs_of_role(fc_roleid, fc_ownerId);
}

/*
 * 对指定OID的表空间进行所有权检查。
 */
bool pg_tablespace_ownercheck(Oid fc_spc_oid, Oid fc_roleid)
{
	HeapTuple	fc_spctuple;
	Oid			fc_spcowner;

	/* 超级用户绕过所有权限检查。 */
	if (superuser_arg(fc_roleid))
		return true;

	/* 搜索 pg_tablespace 的 syscache */
	fc_spctuple = SearchSysCache1(TABLESPACEOID, ObjectIdGetDatum(fc_spc_oid));
	if (!HeapTupleIsValid(fc_spctuple))
		ereport(ERROR,
				(errcode(ERRCODE_UNDEFINED_OBJECT),
				 errmsg("tablespace with OID %u does not exist", fc_spc_oid)));

	fc_spcowner = ((Form_pg_tablespace) GETSTRUCT(fc_spctuple))->spcowner;

	ReleaseSysCache(fc_spctuple);

	return has_privs_of_role(fc_roleid, fc_spcowner);
}

/*
 * 对指定OID的运算符类别进行所有权检查。
 */
bool pg_opclass_ownercheck(Oid fc_opc_oid, Oid fc_roleid)
{
	HeapTuple	fc_tuple;
	Oid			fc_ownerId;

	/* 超级用户绕过所有权限检查。 */
	if (superuser_arg(fc_roleid))
		return true;

	fc_tuple = SearchSysCache1(CLAOID, ObjectIdGetDatum(fc_opc_oid));
	if (!HeapTupleIsValid(fc_tuple))
		ereport(ERROR,
				(errcode(ERRCODE_UNDEFINED_OBJECT),
				 errmsg("operator class with OID %u does not exist",
						fc_opc_oid)));

	fc_ownerId = ((Form_pg_opclass) GETSTRUCT(fc_tuple))->opcowner;

	ReleaseSysCache(fc_tuple);

	return has_privs_of_role(fc_roleid, fc_ownerId);
}

/*
 * 对指定OID的运算符家族进行所有权检查。
 */
bool pg_opfamily_ownercheck(Oid fc_opf_oid, Oid fc_roleid)
{
	HeapTuple	fc_tuple;
	Oid			fc_ownerId;

	/* 超级用户绕过所有权限检查。 */
	if (superuser_arg(fc_roleid))
		return true;

	fc_tuple = SearchSysCache1(OPFAMILYOID, ObjectIdGetDatum(fc_opf_oid));
	if (!HeapTupleIsValid(fc_tuple))
		ereport(ERROR,
				(errcode(ERRCODE_UNDEFINED_OBJECT),
				 errmsg("operator family with OID %u does not exist",
						fc_opf_oid)));

	fc_ownerId = ((Form_pg_opfamily) GETSTRUCT(fc_tuple))->opfowner;

	ReleaseSysCache(fc_tuple);

	return has_privs_of_role(fc_roleid, fc_ownerId);
}

/*
 * 对指定OID的文本搜索字典进行所有权检查。
 */
bool pg_ts_dict_ownercheck(Oid fc_dict_oid, Oid fc_roleid)
{
	HeapTuple	fc_tuple;
	Oid			fc_ownerId;

	/* 超级用户绕过所有权限检查。 */
	if (superuser_arg(fc_roleid))
		return true;

	fc_tuple = SearchSysCache1(TSDICTOID, ObjectIdGetDatum(fc_dict_oid));
	if (!HeapTupleIsValid(fc_tuple))
		ereport(ERROR,
				(errcode(ERRCODE_UNDEFINED_OBJECT),
				 errmsg("text search dictionary with OID %u does not exist",
						fc_dict_oid)));

	fc_ownerId = ((Form_pg_ts_dict) GETSTRUCT(fc_tuple))->dictowner;

	ReleaseSysCache(fc_tuple);

	return has_privs_of_role(fc_roleid, fc_ownerId);
}

/*
 * 对指定OID的文本搜索配置进行所有权检查。
 */
bool pg_ts_config_ownercheck(Oid fc_cfg_oid, Oid fc_roleid)
{
	HeapTuple	fc_tuple;
	Oid			fc_ownerId;

	/* 超级用户绕过所有权限检查。 */
	if (superuser_arg(fc_roleid))
		return true;

	fc_tuple = SearchSysCache1(TSCONFIGOID, ObjectIdGetDatum(fc_cfg_oid));
	if (!HeapTupleIsValid(fc_tuple))
		ereport(ERROR,
				(errcode(ERRCODE_UNDEFINED_OBJECT),
				 errmsg("text search configuration with OID %u does not exist",
						fc_cfg_oid)));

	fc_ownerId = ((Form_pg_ts_config) GETSTRUCT(fc_tuple))->cfgowner;

	ReleaseSysCache(fc_tuple);

	return has_privs_of_role(fc_roleid, fc_ownerId);
}

/*
 * 对指定OID的外部数据封装器进行所有权检查。
 */
bool pg_foreign_data_wrapper_ownercheck(Oid fc_srv_oid, Oid fc_roleid)
{
	HeapTuple	fc_tuple;
	Oid			fc_ownerId;

	/* 超级用户绕过所有权限检查。 */
	if (superuser_arg(fc_roleid))
		return true;

	fc_tuple = SearchSysCache1(FOREIGNDATAWRAPPEROID, ObjectIdGetDatum(fc_srv_oid));
	if (!HeapTupleIsValid(fc_tuple))
		ereport(ERROR,
				(errcode(ERRCODE_UNDEFINED_OBJECT),
				 errmsg("foreign-data wrapper with OID %u does not exist",
						fc_srv_oid)));

	fc_ownerId = ((Form_pg_foreign_data_wrapper) GETSTRUCT(fc_tuple))->fdwowner;

	ReleaseSysCache(fc_tuple);

	return has_privs_of_role(fc_roleid, fc_ownerId);
}

/*
 * 对指定OID的外部服务器进行所有权检查。
 */
bool pg_foreign_server_ownercheck(Oid fc_srv_oid, Oid fc_roleid)
{
	HeapTuple	fc_tuple;
	Oid			fc_ownerId;

	/* 超级用户绕过所有权限检查。 */
	if (superuser_arg(fc_roleid))
		return true;

	fc_tuple = SearchSysCache1(FOREIGNSERVEROID, ObjectIdGetDatum(fc_srv_oid));
	if (!HeapTupleIsValid(fc_tuple))
		ereport(ERROR,
				(errcode(ERRCODE_UNDEFINED_OBJECT),
				 errmsg("foreign server with OID %u does not exist",
						fc_srv_oid)));

	fc_ownerId = ((Form_pg_foreign_server) GETSTRUCT(fc_tuple))->srvowner;

	ReleaseSysCache(fc_tuple);

	return has_privs_of_role(fc_roleid, fc_ownerId);
}

/*
 * 对指定OID的事件触发器进行所有权检查。
 */
bool pg_event_trigger_ownercheck(Oid fc_et_oid, Oid fc_roleid)
{
	HeapTuple	fc_tuple;
	Oid			fc_ownerId;

	/* 超级用户绕过所有权限检查。 */
	if (superuser_arg(fc_roleid))
		return true;

	fc_tuple = SearchSysCache1(EVENTTRIGGEROID, ObjectIdGetDatum(fc_et_oid));
	if (!HeapTupleIsValid(fc_tuple))
		ereport(ERROR,
				(errcode(ERRCODE_UNDEFINED_OBJECT),
				 errmsg("event trigger with OID %u does not exist",
						fc_et_oid)));

	fc_ownerId = ((Form_pg_event_trigger) GETSTRUCT(fc_tuple))->evtowner;

	ReleaseSysCache(fc_tuple);

	return has_privs_of_role(fc_roleid, fc_ownerId);
}

/*
 * 对指定OID的数据库进行所有权检查。
 */
bool pg_database_ownercheck(Oid fc_db_oid, Oid fc_roleid)
{
	HeapTuple	fc_tuple;
	Oid			fc_dba;

	/* 超级用户绕过所有权限检查。 */
	if (superuser_arg(fc_roleid))
		return true;

	fc_tuple = SearchSysCache1(DATABASEOID, ObjectIdGetDatum(fc_db_oid));
	if (!HeapTupleIsValid(fc_tuple))
		ereport(ERROR,
				(errcode(ERRCODE_UNDEFINED_DATABASE),
				 errmsg("database with OID %u does not exist", fc_db_oid)));

	fc_dba = ((Form_pg_database) GETSTRUCT(fc_tuple))->datdba;

	ReleaseSysCache(fc_tuple);

	return has_privs_of_role(fc_roleid, fc_dba);
}

/*
 * 对指定OID的排序规则进行所有权检查。
 */
bool pg_collation_ownercheck(Oid fc_coll_oid, Oid fc_roleid)
{
	HeapTuple	fc_tuple;
	Oid			fc_ownerId;

	/* 超级用户绕过所有权限检查。 */
	if (superuser_arg(fc_roleid))
		return true;

	fc_tuple = SearchSysCache1(COLLOID, ObjectIdGetDatum(fc_coll_oid));
	if (!HeapTupleIsValid(fc_tuple))
		ereport(ERROR,
				(errcode(ERRCODE_UNDEFINED_OBJECT),
				 errmsg("collation with OID %u does not exist", fc_coll_oid)));

	fc_ownerId = ((Form_pg_collation) GETSTRUCT(fc_tuple))->collowner;

	ReleaseSysCache(fc_tuple);

	return has_privs_of_role(fc_roleid, fc_ownerId);
}

/*
 * 对指定OID的转换进行所有权检查。
 */
bool pg_conversion_ownercheck(Oid fc_conv_oid, Oid fc_roleid)
{
	HeapTuple	fc_tuple;
	Oid			fc_ownerId;

	/* 超级用户绕过所有权限检查。 */
	if (superuser_arg(fc_roleid))
		return true;

	fc_tuple = SearchSysCache1(CONVOID, ObjectIdGetDatum(fc_conv_oid));
	if (!HeapTupleIsValid(fc_tuple))
		ereport(ERROR,
				(errcode(ERRCODE_UNDEFINED_OBJECT),
				 errmsg("conversion with OID %u does not exist", fc_conv_oid)));

	fc_ownerId = ((Form_pg_conversion) GETSTRUCT(fc_tuple))->conowner;

	ReleaseSysCache(fc_tuple);

	return has_privs_of_role(fc_roleid, fc_ownerId);
}

/*
 * 对指定OID的扩展进行所有权检查。
 */
bool pg_extension_ownercheck(Oid fc_ext_oid, Oid fc_roleid)
{
	Relation	pg_extension;
	ScanKeyData fc_entry[1];
	SysScanDesc fc_scan;
	HeapTuple	fc_tuple;
	Oid			fc_ownerId;

	/* 超级用户绕过所有权限检查。 */
	if (superuser_arg(fc_roleid))
		return true;

	/* pg_extension没有系统缓存，因此以困难的方式处理 */
	pg_extension = table_open(ExtensionRelationId, AccessShareLock);

	ScanKeyInit(&fc_entry[0],
				Anum_pg_extension_oid,
				BTEqualStrategyNumber, F_OIDEQ,
				ObjectIdGetDatum(fc_ext_oid));

	fc_scan = systable_beginscan(pg_extension,
							  ExtensionOidIndexId, true,
							  NULL, 1, fc_entry);

	fc_tuple = systable_getnext(fc_scan);
	if (!HeapTupleIsValid(fc_tuple))
		ereport(ERROR,
				(errcode(ERRCODE_UNDEFINED_OBJECT),
				 errmsg("extension with OID %u does not exist", fc_ext_oid)));

	fc_ownerId = ((Form_pg_extension) GETSTRUCT(fc_tuple))->extowner;

	systable_endscan(fc_scan);
	table_close(pg_extension, AccessShareLock);

	return has_privs_of_role(fc_roleid, fc_ownerId);
}

/*
 * 对指定OID的发布进行所有权检查。
 */
bool pg_publication_ownercheck(Oid fc_pub_oid, Oid fc_roleid)
{
	HeapTuple	fc_tuple;
	Oid			fc_ownerId;

	/* 超级用户绕过所有权限检查。 */
	if (superuser_arg(fc_roleid))
		return true;

	fc_tuple = SearchSysCache1(PUBLICATIONOID, ObjectIdGetDatum(fc_pub_oid));
	if (!HeapTupleIsValid(fc_tuple))
		ereport(ERROR,
				(errcode(ERRCODE_UNDEFINED_OBJECT),
				 errmsg("publication with OID %u does not exist", fc_pub_oid)));

	fc_ownerId = ((Form_pg_publication) GETSTRUCT(fc_tuple))->pubowner;

	ReleaseSysCache(fc_tuple);

	return has_privs_of_role(fc_roleid, fc_ownerId);
}

/*
 * 对指定OID的订阅进行所有权检查。
 */
bool pg_subscription_ownercheck(Oid fc_sub_oid, Oid fc_roleid)
{
	HeapTuple	fc_tuple;
	Oid			fc_ownerId;

	/* 超级用户绕过所有权限检查。 */
	if (superuser_arg(fc_roleid))
		return true;

	fc_tuple = SearchSysCache1(SUBSCRIPTIONOID, ObjectIdGetDatum(fc_sub_oid));
	if (!HeapTupleIsValid(fc_tuple))
		ereport(ERROR,
				(errcode(ERRCODE_UNDEFINED_OBJECT),
				 errmsg("subscription with OID %u does not exist", fc_sub_oid)));

	fc_ownerId = ((Form_pg_subscription) GETSTRUCT(fc_tuple))->subowner;

	ReleaseSysCache(fc_tuple);

	return has_privs_of_role(fc_roleid, fc_ownerId);
}

/*
 * 对指定OID的统计对象进行所有权检查。
 */
bool pg_statistics_object_ownercheck(Oid fc_stat_oid, Oid fc_roleid)
{
	HeapTuple	fc_tuple;
	Oid			fc_ownerId;

	/* 超级用户绕过所有权限检查。 */
	if (superuser_arg(fc_roleid))
		return true;

	fc_tuple = SearchSysCache1(STATEXTOID, ObjectIdGetDatum(fc_stat_oid));
	if (!HeapTupleIsValid(fc_tuple))
		ereport(ERROR,
				(errcode(ERRCODE_UNDEFINED_OBJECT),
				 errmsg("statistics object with OID %u does not exist",
						fc_stat_oid)));

	fc_ownerId = ((Form_pg_statistic_ext) GETSTRUCT(fc_tuple))->stxowner;

	ReleaseSysCache(fc_tuple);

	return has_privs_of_role(fc_roleid, fc_ownerId);
}

/*
 * 检查指定角色是否具有CREATEROLE权限（或是超级用户）
 *
 * 注意：角色并没有所有者；我们在需要角色的所有权权限检查
 * 的地方使用这个测试。
 * 确保将其应用于尝试执行操作的角色，而不是被操作的角色！ 
 * 还要注意，如果目标角色是超级用户，这通常不应被视为
 * 足够的权限。
 * （我们在这里不处理这一考虑，因为我们希望为此类情况
 * 提供单独的错误消息，因此调用者必须自己处理。）
 */
bool has_createrole_privilege(Oid fc_roleid)
{
	bool		fc_result = false;
	HeapTuple	fc_utup;

	/* 超级用户绕过所有权限检查。 */
	if (superuser_arg(fc_roleid))
		return true;

	fc_utup = SearchSysCache1(AUTHOID, ObjectIdGetDatum(fc_roleid));
	if (HeapTupleIsValid(fc_utup))
	{
		fc_result = ((Form_pg_authid) GETSTRUCT(fc_utup))->rolcreaterole;
		ReleaseSysCache(fc_utup);
	}
	return fc_result;
}

bool has_bypassrls_privilege(Oid fc_roleid)
{
	bool		fc_result = false;
	HeapTuple	fc_utup;

	/* 超级用户绕过所有权限检查。 */
	if (superuser_arg(fc_roleid))
		return true;

	fc_utup = SearchSysCache1(AUTHOID, ObjectIdGetDatum(fc_roleid));
	if (HeapTupleIsValid(fc_utup))
	{
		fc_result = ((Form_pg_authid) GETSTRUCT(fc_utup))->rolbypassrls;
		ReleaseSysCache(fc_utup);
	}
	return fc_result;
}

/*
 * 获取指定角色、命名空间和对象类型的pg_default_acl条目
 * （对象类型必须以pg_default_acl的编码给出）。
 * 如果没有这样的条目，则返回NULL。
 */
static Acl * get_default_acl_internal(Oid fc_roleId, Oid fc_nsp_oid, char fc_objtype)
{
	Acl		   *fc_result = NULL;
	HeapTuple	fc_tuple;

	fc_tuple = SearchSysCache3(DEFACLROLENSPOBJ,
							ObjectIdGetDatum(fc_roleId),
							ObjectIdGetDatum(fc_nsp_oid),
							CharGetDatum(fc_objtype));

	if (HeapTupleIsValid(fc_tuple))
	{
		Datum		fc_aclDatum;
		bool		fc_isNull;

		fc_aclDatum = SysCacheGetAttr(DEFACLROLENSPOBJ, fc_tuple,
								   Anum_pg_default_acl_defaclacl,
								   &fc_isNull);
		if (!fc_isNull)
			fc_result = DatumGetAclPCopy(fc_aclDatum);
		ReleaseSysCache(fc_tuple);
	}

	return fc_result;
}

/*
 * 获取在给定模式内新创建对象的默认权限
 *
 * 如果应使用内置系统默认值，则返回NULL。
 *
 * 如果结果不为NULL，则调用者必须在知道新对象的OID后
 * 调用recordDependencyOnNewAcl。
 */
Acl * get_user_default_acl(ObjectType fc_objtype, Oid fc_ownerId, Oid fc_nsp_oid)
{
	Acl		   *fc_result;
	Acl		   *fc_glob_acl;
	Acl		   *fc_schema_acl;
	Acl		   *fc_def_acl;
	char		fc_defaclobjtype;

	/*
	 * 在引导期间使用NULL，因为pg_default_acl可能还不存在。
	 */
	if (IsBootstrapProcessingMode())
		return NULL;

	/* 检查对象类型是否在pg_default_acl中被支持 */
	switch (fc_objtype)
	{
		case OBJECT_TABLE:
			fc_defaclobjtype = DEFACLOBJ_RELATION;
			break;

		case OBJECT_SEQUENCE:
			fc_defaclobjtype = DEFACLOBJ_SEQUENCE;
			break;

		case OBJECT_FUNCTION:
			fc_defaclobjtype = DEFACLOBJ_FUNCTION;
			break;

		case OBJECT_TYPE:
			fc_defaclobjtype = DEFACLOBJ_TYPE;
			break;

		case OBJECT_SCHEMA:
			fc_defaclobjtype = DEFACLOBJ_NAMESPACE;
			break;

		default:
			return NULL;
	}

	/* 查找相关的pg_default_acl条目 */
	fc_glob_acl = get_default_acl_internal(fc_ownerId, InvalidOid, fc_defaclobjtype);
	fc_schema_acl = get_default_acl_internal(fc_ownerId, fc_nsp_oid, fc_defaclobjtype);

	/* 如果两个条目都不存在则快速退出 */
	if (fc_glob_acl == NULL && fc_schema_acl == NULL)
		return NULL;

	/* 我们也需要知道硬编码的默认值 */
	fc_def_acl = acldefault(fc_objtype, fc_ownerId);

	/* 如果没有全局条目，则替换为硬编码的默认值 */
	if (fc_glob_acl == NULL)
		fc_glob_acl = fc_def_acl;

	/* 合并任何每个模式的特权 */
	fc_result = aclmerge(fc_glob_acl, fc_schema_acl, fc_ownerId);

	/*
	 * 为了提高效率，如果结果等于默认值，我们希望返回 NULL。
	 * 这需要对两个数组进行排序以准确比较。
	 */
	aclitemsort(fc_result);
	aclitemsort(fc_def_acl);
	if (aclequal(fc_result, fc_def_acl))
		fc_result = NULL;

	return fc_result;
}

/*
 * 记录新对象的 ACL 中提到的角色的依赖关系。
 */
void recordDependencyOnNewAcl(Oid fc_classId, Oid fc_objectId, int32 fc_objsubId,
						 Oid fc_ownerId, Acl *fc_acl)
{
	int			fc_nmembers;
	Oid		   *fc_members;

	/* 如果 ACL 是默认的，则无需执行任何操作 */
	if (fc_acl == NULL)
		return;

	/* 提取 ACL 中提到的角色 */
	fc_nmembers = aclmembers(fc_acl, &fc_members);

	/* 更新共享依赖 ACL 信息 */
	updateAclDependencies(fc_classId, fc_objectId, fc_objsubId,
						  fc_ownerId,
						  0, NULL,
						  fc_nmembers, fc_members);
}

/*
 * 记录传入的顶级对象的初始特权。
 *
 * 对于传入的对象，这将记录其 ACL（如果有）和任何子对象（例如：列）的 ACL
 * 到 pg_init_privs 中。
 *
 * 任何与其相关并可添加到扩展中的新类型对象都应添加到下面的 if-else 树中。
 */
void recordExtObjInitPriv(Oid fc_objoid, Oid fc_classoid)
{
	/*
	 * pg_class / pg_attribute
	 *
	 * 如果这是一个关系，则需要查看是否有任何子对象
	 * （例如：列）并且如果有，确保为每个子对象调用
	 * recordExtensionInitPrivWorker()。
	 */
	if (fc_classoid == RelationRelationId)
	{
		Form_pg_class pg_class_tuple;
		Datum		fc_aclDatum;
		bool		fc_isNull;
		HeapTuple	fc_tuple;

		fc_tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(fc_objoid));
		if (!HeapTupleIsValid(fc_tuple))
			elog(ERROR, "cache lookup failed for relation %u", fc_objoid);
		pg_class_tuple = (Form_pg_class) GETSTRUCT(fc_tuple);

		/*
		 * 索引没有权限，复合类型的 pg_class 行也没有。
		 * （考虑到 ALTER EXTENSION ADD 的限制，这些情况是不可达的，
		 * 但我们还是检查一下。）
		 */
		if (pg_class_tuple->relkind == RELKIND_INDEX ||
			pg_class_tuple->relkind == RELKIND_PARTITIONED_INDEX ||
			pg_class_tuple->relkind == RELKIND_COMPOSITE_TYPE)
		{
			ReleaseSysCache(fc_tuple);
			return;
		}

		/*
		 * 如果这不是一个序列，则它可能会有与之相关的列级 ACL。
		 */
		if (pg_class_tuple->relkind != RELKIND_SEQUENCE)
		{
			AttrNumber	fc_curr_att;
			AttrNumber	fc_nattrs = pg_class_tuple->relnatts;

			for (fc_curr_att = 1; fc_curr_att <= fc_nattrs; fc_curr_att++)
			{
				HeapTuple	fc_attTuple;
				Datum		fc_attaclDatum;

				fc_attTuple = SearchSysCache2(ATTNUM,
										   ObjectIdGetDatum(fc_objoid),
										   Int16GetDatum(fc_curr_att));

				if (!HeapTupleIsValid(fc_attTuple))
					continue;

				/* 忽略已删除的列 */
				if (((Form_pg_attribute) GETSTRUCT(fc_attTuple))->attisdropped)
				{
					ReleaseSysCache(fc_attTuple);
					continue;
				}

				fc_attaclDatum = SysCacheGetAttr(ATTNUM, fc_attTuple,
											  Anum_pg_attribute_attacl,
											  &fc_isNull);

				/* 对于 NULL ACL 不需要执行任何操作 */
				if (fc_isNull)
				{
					ReleaseSysCache(fc_attTuple);
					continue;
				}

				fc_recordExtensionInitPrivWorker(fc_objoid, fc_classoid, fc_curr_att,
											  DatumGetAclP(fc_attaclDatum));

				ReleaseSysCache(fc_attTuple);
			}
		}

		fc_aclDatum = SysCacheGetAttr(RELOID, fc_tuple, Anum_pg_class_relacl,
								   &fc_isNull);

		/* 添加传入的顶级对象的记录（如果有） */
		if (!fc_isNull)
			fc_recordExtensionInitPrivWorker(fc_objoid, fc_classoid, 0,
										  DatumGetAclP(fc_aclDatum));

		ReleaseSysCache(fc_tuple);
	}
	/* pg_foreign_data_wrapper */
	else if (fc_classoid == ForeignDataWrapperRelationId)
	{
		Datum		fc_aclDatum;
		bool		fc_isNull;
		HeapTuple	fc_tuple;

		fc_tuple = SearchSysCache1(FOREIGNDATAWRAPPEROID,
								ObjectIdGetDatum(fc_objoid));
		if (!HeapTupleIsValid(fc_tuple))
			elog(ERROR, "cache lookup failed for foreign data wrapper %u",
				 fc_objoid);

		fc_aclDatum = SysCacheGetAttr(FOREIGNDATAWRAPPEROID, fc_tuple,
								   Anum_pg_foreign_data_wrapper_fdwacl,
								   &fc_isNull);

		/* 添加传入的顶级对象的记录（如果有） */
		if (!fc_isNull)
			fc_recordExtensionInitPrivWorker(fc_objoid, fc_classoid, 0,
										  DatumGetAclP(fc_aclDatum));

		ReleaseSysCache(fc_tuple);
	}
	/* pg_foreign_server */
	else if (fc_classoid == ForeignServerRelationId)
	{
		Datum		fc_aclDatum;
		bool		fc_isNull;
		HeapTuple	fc_tuple;

		fc_tuple = SearchSysCache1(FOREIGNSERVEROID, ObjectIdGetDatum(fc_objoid));
		if (!HeapTupleIsValid(fc_tuple))
			elog(ERROR, "cache lookup failed for foreign server %u",
				 fc_objoid);

		fc_aclDatum = SysCacheGetAttr(FOREIGNSERVEROID, fc_tuple,
								   Anum_pg_foreign_server_srvacl,
								   &fc_isNull);

		/* 添加传入的顶级对象的记录（如果有） */
		if (!fc_isNull)
			fc_recordExtensionInitPrivWorker(fc_objoid, fc_classoid, 0,
										  DatumGetAclP(fc_aclDatum));

		ReleaseSysCache(fc_tuple);
	}
	/* pg_language */
	else if (fc_classoid == LanguageRelationId)
	{
		Datum		fc_aclDatum;
		bool		fc_isNull;
		HeapTuple	fc_tuple;

		fc_tuple = SearchSysCache1(LANGOID, ObjectIdGetDatum(fc_objoid));
		if (!HeapTupleIsValid(fc_tuple))
			elog(ERROR, "cache lookup failed for language %u", fc_objoid);

		fc_aclDatum = SysCacheGetAttr(LANGOID, fc_tuple, Anum_pg_language_lanacl,
								   &fc_isNull);

		/* 添加传入的顶级对象的记录（如果有） */
		if (!fc_isNull)
			fc_recordExtensionInitPrivWorker(fc_objoid, fc_classoid, 0,
										  DatumGetAclP(fc_aclDatum));

		ReleaseSysCache(fc_tuple);
	}
	else if (fc_classoid == LargeObjectRelationId)
	{
		/* 对于大对象，我们必须查询 pg_largeobject_metadata */
		Datum		fc_aclDatum;
		bool		fc_isNull;
		HeapTuple	fc_tuple;
		ScanKeyData fc_entry[1];
		SysScanDesc fc_scan;
		Relation	fc_relation;

		/*
		 * 注意：这是死代码，因为我们不允许大对象
		 * 成为扩展成员。但这似乎值得保留，以防
		 * 未来调用此函数的某些情况需要它。
		 */
		fc_relation = table_open(LargeObjectMetadataRelationId, RowExclusiveLock);

		/* pg_largeobject_metadata 没有 syscache */
		ScanKeyInit(&fc_entry[0],
					Anum_pg_largeobject_metadata_oid,
					BTEqualStrategyNumber, F_OIDEQ,
					ObjectIdGetDatum(fc_objoid));

		fc_scan = systable_beginscan(fc_relation,
								  LargeObjectMetadataOidIndexId, true,
								  NULL, 1, fc_entry);

		fc_tuple = systable_getnext(fc_scan);
		if (!HeapTupleIsValid(fc_tuple))
			elog(ERROR, "could not find tuple for large object %u", fc_objoid);

		fc_aclDatum = heap_getattr(fc_tuple,
								Anum_pg_largeobject_metadata_lomacl,
								RelationGetDescr(fc_relation), &fc_isNull);

		/* 添加传入的顶级对象的记录（如果有） */
		if (!fc_isNull)
			fc_recordExtensionInitPrivWorker(fc_objoid, fc_classoid, 0,
										  DatumGetAclP(fc_aclDatum));

		systable_endscan(fc_scan);
	}
	/* pg_namespace */
	else if (fc_classoid == NamespaceRelationId)
	{
		Datum		fc_aclDatum;
		bool		fc_isNull;
		HeapTuple	fc_tuple;

		fc_tuple = SearchSysCache1(NAMESPACEOID, ObjectIdGetDatum(fc_objoid));
		if (!HeapTupleIsValid(fc_tuple))
			elog(ERROR, "cache lookup failed for schema %u", fc_objoid);

		fc_aclDatum = SysCacheGetAttr(NAMESPACEOID, fc_tuple,
								   Anum_pg_namespace_nspacl, &fc_isNull);

		/* 添加传入的顶级对象的记录（如果有） */
		if (!fc_isNull)
			fc_recordExtensionInitPrivWorker(fc_objoid, fc_classoid, 0,
										  DatumGetAclP(fc_aclDatum));

		ReleaseSysCache(fc_tuple);
	}
	/* pg_proc */
	else if (fc_classoid == ProcedureRelationId)
	{
		Datum		fc_aclDatum;
		bool		fc_isNull;
		HeapTuple	fc_tuple;

		fc_tuple = SearchSysCache1(PROCOID, ObjectIdGetDatum(fc_objoid));
		if (!HeapTupleIsValid(fc_tuple))
			elog(ERROR, "cache lookup failed for function %u", fc_objoid);

		fc_aclDatum = SysCacheGetAttr(PROCOID, fc_tuple, Anum_pg_proc_proacl,
								   &fc_isNull);

		/* 添加传入的顶级对象的记录（如果有） */
		if (!fc_isNull)
			fc_recordExtensionInitPrivWorker(fc_objoid, fc_classoid, 0,
										  DatumGetAclP(fc_aclDatum));

		ReleaseSysCache(fc_tuple);
	}
	/* pg_type */
	else if (fc_classoid == TypeRelationId)
	{
		Datum		fc_aclDatum;
		bool		fc_isNull;
		HeapTuple	fc_tuple;

		fc_tuple = SearchSysCache1(TYPEOID, ObjectIdGetDatum(fc_objoid));
		if (!HeapTupleIsValid(fc_tuple))
			elog(ERROR, "cache lookup failed for type %u", fc_objoid);

		fc_aclDatum = SysCacheGetAttr(TYPEOID, fc_tuple, Anum_pg_type_typacl,
								   &fc_isNull);

		/* 添加传入的顶级对象的记录（如果有） */
		if (!fc_isNull)
			fc_recordExtensionInitPrivWorker(fc_objoid, fc_classoid, 0,
										  DatumGetAclP(fc_aclDatum));

		ReleaseSysCache(fc_tuple);
	}
	else if (fc_classoid == AccessMethodRelationId ||
			 fc_classoid == AggregateRelationId ||
			 fc_classoid == CastRelationId ||
			 fc_classoid == CollationRelationId ||
			 fc_classoid == ConversionRelationId ||
			 fc_classoid == EventTriggerRelationId ||
			 fc_classoid == OperatorRelationId ||
			 fc_classoid == OperatorClassRelationId ||
			 fc_classoid == OperatorFamilyRelationId ||
			 fc_classoid == NamespaceRelationId ||
			 fc_classoid == TSConfigRelationId ||
			 fc_classoid == TSDictionaryRelationId ||
			 fc_classoid == TSParserRelationId ||
			 fc_classoid == TSTemplateRelationId ||
			 fc_classoid == TransformRelationId
		)
	{
		/* 这些对象类型没有 ACL，因此不执行任何操作。 */
	}

	/*
	 * 如果我们得到一个扩展不支持或我们不认的类 OID，请投诉。
	 */
	else
	{
		elog(ERROR, "unrecognized or unsupported class OID: %u", fc_classoid);
	}
}

/*
 * 对于传入的对象，从 pg_init_privs 中删除其 ACL 和任何对象子 ID
 * （通过 recordExtensionInitPrivWorker()）。
 */
void removeExtObjInitPriv(Oid fc_objoid, Oid fc_classoid)
{
	/*
	 * 如果这是一个关系，则需要查看是否有任何子对象
	 * （例如：列）并且如果有，确保为每个子对象调用
	 * recordExtensionInitPrivWorker()。
	 */
	if (fc_classoid == RelationRelationId)
	{
		Form_pg_class pg_class_tuple;
		HeapTuple	fc_tuple;

		fc_tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(fc_objoid));
		if (!HeapTupleIsValid(fc_tuple))
			elog(ERROR, "cache lookup failed for relation %u", fc_objoid);
		pg_class_tuple = (Form_pg_class) GETSTRUCT(fc_tuple);

		/*
		 * 索引没有权限，复合类型的 pg_class 行也没有。
		 * （考虑到 ALTER EXTENSION DROP 的限制，这些情况是不可达的，
		 * 但我们还是检查一下。）
		 */
		if (pg_class_tuple->relkind == RELKIND_INDEX ||
			pg_class_tuple->relkind == RELKIND_PARTITIONED_INDEX ||
			pg_class_tuple->relkind == RELKIND_COMPOSITE_TYPE)
		{
			ReleaseSysCache(fc_tuple);
			return;
		}

		/*
		 * 如果这不是一个序列，则它可能会有与之相关的列级 ACL。
		 */
		if (pg_class_tuple->relkind != RELKIND_SEQUENCE)
		{
			AttrNumber	fc_curr_att;
			AttrNumber	fc_nattrs = pg_class_tuple->relnatts;

			for (fc_curr_att = 1; fc_curr_att <= fc_nattrs; fc_curr_att++)
			{
				HeapTuple	fc_attTuple;

				fc_attTuple = SearchSysCache2(ATTNUM,
										   ObjectIdGetDatum(fc_objoid),
										   Int16GetDatum(fc_curr_att));

				if (!HeapTupleIsValid(fc_attTuple))
					continue;

				/* 在移除时，移除所有条目，包括已删除的列 */

				fc_recordExtensionInitPrivWorker(fc_objoid, fc_classoid, fc_curr_att, NULL);

				ReleaseSysCache(fc_attTuple);
			}
		}

		ReleaseSysCache(fc_tuple);
	}

	/* 移除传入的顶级对象的记录（如果有） */
	fc_recordExtensionInitPrivWorker(fc_objoid, fc_classoid, 0, NULL);
}

/*
 * 记录扩展对象的初始 ACL
 *
 * 可以随时调用，我们检查是否设置了 'creating_extension'，如果没有，
 * 立即退出。
 *
 * 传入对象 OID、类的 OID（对象定义的表的 OID）和对象的 'sub' id（objsubid），如果有。
 * 如果没有 'sub' id（目前仅用于表的列），则传入 '0'。最后，传入完整的 ACL 进行存储。
 *
 * 如果这个对象/子对象已有 ACL，则我们将用传入的内容替换它。
 *
 * 对于 'new_acl' 传入 NULL 将导致对象条目被移除（如果找到的话）。
 */
static void fc_recordExtensionInitPriv(Oid fc_objoid, Oid fc_classoid, int fc_objsubid, Acl *fc_new_acl)
{
	/*
	 * 通常，我们仅在创建扩展时记录初始特权，但由于在使用 pg_upgrade 进行二进制升级时
	 * 我们实际上不使用 CREATE EXTENSION，因此有一个变量让我们知道在此期间发出的
	 * GRANT 和 REVOKE 语句是针对扩展对象的初始特权，因此我们需要记录它们。
	 */
	if (!creating_extension && !binary_upgrade_record_init_privs)
		return;

	fc_recordExtensionInitPrivWorker(fc_objoid, fc_classoid, fc_objsubid, fc_new_acl);
}

/*
 * 记录扩展对象的初始 ACL，工作者。
 *
 * 这将完全替换传入对象的整个 ACL，因此确保传入完整的新 ACL。
 *
 * 通常来说，不要直接使用此函数，而是使用 recordExtensionInitPriv()，它会检查
 * 'creating_extension' 是否被设置。这个函数不检查 'creating_extension' 的设置，
 * 因为它也在通过 ALTER EXTENSION ... ADD/DROP 增加或移除对象时使用。
 */
static void fc_recordExtensionInitPrivWorker(Oid fc_objoid, Oid fc_classoid, int fc_objsubid, Acl *fc_new_acl)
{
	Relation	fc_relation;
	ScanKeyData fc_key[3];
	SysScanDesc fc_scan;
	HeapTuple	fc_tuple;
	HeapTuple	fc_oldtuple;

	fc_relation = table_open(InitPrivsRelationId, RowExclusiveLock);

	ScanKeyInit(&fc_key[0],
				Anum_pg_init_privs_objoid,
				BTEqualStrategyNumber, F_OIDEQ,
				ObjectIdGetDatum(fc_objoid));
	ScanKeyInit(&fc_key[1],
				Anum_pg_init_privs_classoid,
				BTEqualStrategyNumber, F_OIDEQ,
				ObjectIdGetDatum(fc_classoid));
	ScanKeyInit(&fc_key[2],
				Anum_pg_init_privs_objsubid,
				BTEqualStrategyNumber, F_INT4EQ,
				Int32GetDatum(fc_objsubid));

	fc_scan = systable_beginscan(fc_relation, InitPrivsObjIndexId, true,
							  NULL, 3, fc_key);

	/* 应该仅存在一个条目或没有条目。 */
	fc_oldtuple = systable_getnext(fc_scan);

	/* 如果找到一个条目，则用最新的ACL更新它。 */
	if (HeapTupleIsValid(fc_oldtuple))
	{
		Datum		fc_values[Natts_pg_init_privs];
		bool		fc_nulls[Natts_pg_init_privs];
		bool		fc_replace[Natts_pg_init_privs];

		/* 如果我们有一个新的ACL要设置，则用它更新行。 */
		if (fc_new_acl)
		{
			MemSet(fc_values, 0, sizeof(fc_values));
			MemSet(fc_nulls, false, sizeof(fc_nulls));
			MemSet(fc_replace, false, sizeof(fc_replace));

			fc_values[Anum_pg_init_privs_initprivs - 1] = PointerGetDatum(fc_new_acl);
			fc_replace[Anum_pg_init_privs_initprivs - 1] = true;

			fc_oldtuple = heap_modify_tuple(fc_oldtuple, RelationGetDescr(fc_relation),
										 fc_values, fc_nulls, fc_replace);

			CatalogTupleUpdate(fc_relation, &fc_oldtuple->t_self, fc_oldtuple);
		}
		else
		{
			/* new_acl为NULL，因此删除我们找到的条目。 */
			CatalogTupleDelete(fc_relation, &fc_oldtuple->t_self);
		}
	}
	else
	{
		Datum		fc_values[Natts_pg_init_privs];
		bool		fc_nulls[Natts_pg_init_privs];

		/*
		 * 仅当新的ACL不为NULL时才添加新条目。
		 *
		 * 如果传入一个NULL ACL且不存在条目，则可以直接
		 * 跳过，不做任何操作。
		 */
		if (fc_new_acl)
		{
			/* 未找到条目，因此添加它。 */
			MemSet(fc_nulls, false, sizeof(fc_nulls));

			fc_values[Anum_pg_init_privs_objoid - 1] = ObjectIdGetDatum(fc_objoid);
			fc_values[Anum_pg_init_privs_classoid - 1] = ObjectIdGetDatum(fc_classoid);
			fc_values[Anum_pg_init_privs_objsubid - 1] = Int32GetDatum(fc_objsubid);

			/* 此函数仅处理扩展的初始权限 */
			fc_values[Anum_pg_init_privs_privtype - 1] =
				CharGetDatum(INITPRIVS_EXTENSION);

			fc_values[Anum_pg_init_privs_initprivs - 1] = PointerGetDatum(fc_new_acl);

			fc_tuple = heap_form_tuple(RelationGetDescr(fc_relation), fc_values, fc_nulls);

			CatalogTupleInsert(fc_relation, fc_tuple);
		}
	}

	systable_endscan(fc_scan);

	/* 防止多次处理对象时发生错误 */
	CommandCounterIncrement();

	table_close(fc_relation, RowExclusiveLock);
}
